汎用型の使い方を教えてあげます(二)
汎用型の基本的な使用は私のこの文章(汎用型の使い方を教えてくれます)を参考にすることができますが、本当に汎用型を使うときは、特に罠に気をつけなければなりません.この記事では、Javaの汎用的なタイプの削除と、タイプの削除がどのような問題をもたらすか、これらの問題をどのように正しく処理するかを紹介します.まず、例を見てみましょう.
私たちのほとんどは
型檫除(type erasure):generic type information is present only at compile time,after which it is erased by the compiler.
簡単に言えば、汎用型を使用すると、具体的な情報が消去されます.汎用コードの内部では、汎用パラメータタイプに関する情報は取得できません.したがって、上記コードでは、
上のコードを見て、私たちはおかしいかもしれませんが、
Manipulator.java
タイプ消去がいろいろな問題をもたらす以上、なぜ消去を使うのかと聞かれるかもしれません.まず、Javaは最初は汎用型をサポートしていないことを理解してください.汎用型はJava SE 5が行った大きな変化である.Java 1.0から汎用型がサポートされている場合、設計者は消去を使用しないに違いありません.したがって,消去を用いるのは,既存のクラスライブラリを破壊することなく汎用をJava言語に組み込むためである.
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<String>();
ArrayList<Integer> intList = new ArrayList<Integer>();
System.out.println(strList.getClass());
System.out.println(intList.getClass());
}
私たちのほとんどは
ArrayList<String>
ArrayList<Integer>
とは違うタイプだと思っています.しかしながら、上記のコードを実行すると、いずれもclass java.util.ArrayList
が返される.どうしてこんな結果になったの??これはタイプ消去の効果のためです.型檫除(type erasure):generic type information is present only at compile time,after which it is erased by the compiler.
簡単に言えば、汎用型を使用すると、具体的な情報が消去されます.汎用コードの内部では、汎用パラメータタイプに関する情報は取得できません.したがって、上記コードでは、
ArrayList<String>
とArrayList<integer>
は同じタイプである.JavaのデザインはC++によって大いに啓発された.Java汎用型はJava SE 5の時に提案されたものです.もしあなたが以前C++を学んだことがあるならば、あなたは発見することができて、いくつか以前C++はすることができて、Javaはできません.汎用的な理解を深めるために、もう一つの例を見てみましょう.C++テンプレートを使用すると、このように書くことができます.#include <iostream>
using namespace std;
template<class T> class Manipulator{
T obj;
public:
Manipulator(T x){
obj = x;
}
void manipulate(){
// 。
obj.f();
}
};
class HasF{
public:
void f(){
cout<<"call to f()";
}
};
int main()
{
HasF hasF;
Manipulator<HasF> man(hasF);
man.manipulate();
// cout << "Hello world!" << endl;
return 0;
}
上のコードを見て、私たちはおかしいかもしれませんが、
obj.f()
obj
どうして知っていますかf()
タイプパラメータT
のために存在しますか?テンプレートクラスをインスタンス化すると、C++コンパイラがチェックし、Manipulator<HasF>
インスタンス化すると、HasF
1個f()
があることがわかるからです.このコードをC++で書くのは簡単です.テンプレートがインスタンス化されると、テンプレートコードはテンプレートパラメータのタイプを知っているからです.では、Java汎用はどのように実現すればいいのでしょうか.C++のように直接呼び出された場合HasF
のメソッドf()
コンパイラがエラーを報告します.タイプ消去のため、汎用パラメータタイプの情報は取得できません.f()
メソッドを呼び出すには、汎用クラスに協力し、汎用クラスの境界を与え、コンパイラがこの境界に従うタイプしか受け入れられないことを通知する必要があります.コードは以下の通りです.HasF.java package genericdemo.typeerasure;
public class HasF {
void f(){
System.out.println("call to f()");
}
}
Manipulator.java
package genericdemo.typeerasure;
import java.util.ArrayList;
public class Manipulator<T extends HasF> {
private T obj;
public Manipulator(T x) {
obj = x;
}
public void method(){
obj.f();
}
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<String>();
ArrayList<Integer> intList = new ArrayList<Integer>();
System.out.println(strList.getClass());
System.out.println(intList.getClass());
System.out.println(strList.getClass() == intList.getClass());
HasF hasF = new HasF();
Manipulator<HasF> man = new Manipulator<HasF>(hasF);
man.method();
}
}
タイプ消去がいろいろな問題をもたらす以上、なぜ消去を使うのかと聞かれるかもしれません.まず、Javaは最初は汎用型をサポートしていないことを理解してください.汎用型はJava SE 5が行った大きな変化である.Java 1.0から汎用型がサポートされている場合、設計者は消去を使用しないに違いありません.したがって,消去を用いるのは,既存のクラスライブラリを破壊することなく汎用をJava言語に組み込むためである.