Java Generic (1)
1汎用クラス 汎用クラスでは、タイプを抽象化できます.最も一般的な汎用クラスはコンテナクラスです.例:
上記の例では、リストにIntegerオブジェクトを追加しようとすると、コンパイルエラーが発生します.コンパイラはタイプチェックを行い、非汎用コンテナクラスを使用する場合によく見られる強制タイプ変換を回避します.汎用クラスは、1つ以上のタイプ変数(type variable)を有するクラスである.以下に簡単な例を示します.
汎用クラスのインスタンスを放出またはキャプチャすることはできず、汎用クラスがThrowableから継承されるのも合法ではないことに注意してください.また、汎用クラスインスタンスの配列も宣言できません.たとえば、次のコードでコンパイルエラーが発生します.
2汎用メソッド 汎用メソッドとは、修飾子と戻りタイプの間のタイプ変数の位置を持つメソッドです.汎用メソッドを呼び出す場合、具体的なタイプの位置はメソッド名の括弧であり、ほとんどの場合、コンパイラは具体的なタイプを判断できるため、具体的なタイプを省略することもできる.以下に簡単な例を示します.
以上の例のprintメソッドでは,TがObjectクラスから継承されるメソッドしか呼び出せない.TをBaseおよびそのサブクラスに限定する場合は、extendsを使用して上限を設定するか、superを使用して下限を設定します(C++ではパラメータ変数を限定できないタイプ).たとえば、次のようになります.
タイプ変数は複数の限定があり、限定タイプは&分割である.限定タイプに複数のインタフェースを含めることができますが、最大1つのクラス(単一継承)があり、1つのクラスがある場合は、最初の限定タイプでなければなりません.例:
3消去
コンパイラは、各汎用タイプに対して元のタイプ(raw type)を自動的に提供します.そのタイプ変数は消去され(したがってJava汎用ではC++テンプレートクラスのコード膨張の問題はない)、限定タイプがない場合はObjectが使用されます.元のデータ型はタイプ変数として使用できません.たとえばBagはありませんが、Bagがあります.タイプ変数は消去されるため、汎用クラスのタイプ変数が何であるかにかかわらず、そのすべてのインスタンスのランタイムクラスは同じです.たとえば、次のコードの出力は次のようになります.
class Bag class Bag true
クラスの静的メンバー変数とメンバーメソッドもクラスのすべてのインスタンスで共有されるため、クラスの静的メンバー変数とメンバーメソッドでタイプ変数を参照することはできません.たとえば、次のコードでコンパイルエラーが発生します.
タイプ変数ではインスタンスを構築できませんが、Class.newInstanceとArray.newInstanceでインスタンスを構築できます.たとえば、次のコードでコンパイルエラーが発生します.
消去後も競合は発生しません.次のコードを考慮します.
以上のBagクラスのタイプ変数が消去されたコードは以下の通りです.
そのequalsメソッドはObject.equals(Object obj)メソッドと競合する.
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(String s: list) {
System.out.println(s);
}
上記の例では、リストにIntegerオブジェクトを追加しようとすると、コンパイルエラーが発生します.コンパイラはタイプチェックを行い、非汎用コンテナクラスを使用する場合によく見られる強制タイプ変換を回避します.汎用クラスは、1つ以上のタイプ変数(type variable)を有するクラスである.以下に簡単な例を示します.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Bag<E> {
private List<E> bag = new ArrayList<E>();
public Iterator<E> iterator() {
return this.bag.iterator();
}
public boolean add(E e) {
return this.bag.add(e);
}
public boolean remove(Object o) {
return this.bag.remove(o);
}
public boolean contains(Object e) {
return this.bag.contains(e);
}
public boolean addAll(Bag<? extends E> b) {
return this.bag.addAll(b.bag);
}
public boolean removeAll(Bag<?> b) {
return this.bag.removeAll(b.bag);
}
public boolean containsAll(Bag<?> b) {
return this.bag.containsAll(b.bag);
}
public void clear() {
this.bag.clear();
}
List<E> getBag() {
return this.bag;
}
}
import java.util.Collections;
import java.util.Comparator;
public class Bags {
public static <E> void sort(Bag<E> b, Comparator<? super E> c) {
Collections.sort(b.getBag(), c);
}
public static <T extends Comparable<? super T>> T max(Bag<T> b) {
return Collections.max(b.getBag());
}
public static <E> void copy(Bag<E> dest, Bag<? extends E> src) {
dest.addAll(src);
}
}
汎用クラスのインスタンスを放出またはキャプチャすることはできず、汎用クラスがThrowableから継承されるのも合法ではないことに注意してください.また、汎用クラスインスタンスの配列も宣言できません.たとえば、次のコードでコンパイルエラーが発生します.
Bag<Integer> b3[] = new Bag<Integer>[10]; // Cannot create a generic array of Bag<Integer>
2汎用メソッド 汎用メソッドとは、修飾子と戻りタイプの間のタイプ変数の位置を持つメソッドです.汎用メソッドを呼び出す場合、具体的なタイプの位置はメソッド名の括弧であり、ほとんどの場合、コンパイラは具体的なタイプを判断できるため、具体的なタイプを省略することもできる.以下に簡単な例を示します.
public class Base {
public String getName() {
return "Base";
}
public static <T> void print(T t) {
System.out.println(t.toString());
}
public static void main(String args[]) {
Base base = new Base();
Base.<Base>print(base);
Base.print(base);
}
}
以上の例のprintメソッドでは,TがObjectクラスから継承されるメソッドしか呼び出せない.TをBaseおよびそのサブクラスに限定する場合は、extendsを使用して上限を設定するか、superを使用して下限を設定します(C++ではパラメータ変数を限定できないタイプ).たとえば、次のようになります.
public static <T extends Base> void print(T t) {
System.out.println(t.getName());
}
タイプ変数は複数の限定があり、限定タイプは&分割である.限定タイプに複数のインタフェースを含めることができますが、最大1つのクラス(単一継承)があり、1つのクラスがある場合は、最初の限定タイプでなければなりません.例:
T extends Comparable & Serializable
3消去
コンパイラは、各汎用タイプに対して元のタイプ(raw type)を自動的に提供します.そのタイプ変数は消去され(したがってJava汎用ではC++テンプレートクラスのコード膨張の問題はない)、限定タイプがない場合はObjectが使用されます.元のデータ型はタイプ変数として使用できません.たとえばBag
Bag<Integer> b1 = new Bag<Integer>();
Bag<String> b2 = new Bag<String>();
System.out.println(b1.getClass());
System.out.println(b2.getClass());
System.out.println(b1.getClass() == b2.getClass());
class Bag class Bag true
クラスの静的メンバー変数とメンバーメソッドもクラスのすべてのインスタンスで共有されるため、クラスの静的メンバー変数とメンバーメソッドでタイプ変数を参照することはできません.たとえば、次のコードでコンパイルエラーが発生します.
public class Bag<E> {
private static E INSTANCE; // Cannot make a static reference to the non-static type E
public static E getInstance() { // Cannot make a static reference to the non-static type E
return INSTANCE;
}
}
タイプ変数ではインスタンスを構築できませんが、Class.newInstanceとArray.newInstanceでインスタンスを構築できます.たとえば、次のコードでコンパイルエラーが発生します.
public Bag() {
E e1 = new E(); // Cannot instantiate the type E
E e2[] = new E[10];
}
消去後も競合は発生しません.次のコードを考慮します.
public class Bag<E> {
public boolean equals(E e) { // Name clash: The method equals(E) of type Bag<E> has the same erasure as equals(Object) of type Object but does not override it
return true;
}
}
以上のBagクラスのタイプ変数が消去されたコードは以下の通りです.
public class Bag<E> {
public boolean equals(Object e) {
return true;
}
}
そのequalsメソッドはObject.equals(Object obj)メソッドと競合する.