因为泛型是通过擦除编译的,所以在运行时类List<Integer>
,List<String>
和 List<List<String>>
全部由一个类实现,即 List
。你可以看到这个使用
反射:
List<Integer> ints = Arrays.asList(1,2,3);
List<String> strings = Arrays.asList("one","two");
assert ints.getClass() == strings.getClass();
这里在运行时与整数列表关联的类与与字符串列表关联的类相同。
其中一个结果是泛型类的静态成员在该类的所有实例上共享,包括不同类型的实例。 类的静态成员不能引用泛型类的类型参数,并且在访问静态成员时,不应该对类名称 进行参数化。
例如,下面是一个类 Cell<T>
,其中每个单元格都有一个整型标识符和一个类型为 T
的值:
class Cell<T> {
private final int id;
private final T value;
private static int count = 0;
private static synchronized int nextId() {
return count++;
}
public Cell(T value) {
this.value=value; id=nextId();
}
public T getValue() {
return value;
}
public int getId() {
return id;
}
public static synchronized int getCount() {
return count;
}
}
静态字段 count
用于为每个单元分配不同的标识符。 静态 nextId
方法被同步,以确保即使在多个线程的情况下也能生成唯一的标识符。 静态 getCount
方
法返回当前计数。
这里是分配包含字符串的单元格和包含整数的单元格的代码,它们分别分配了标识符 0
和 1
:
Cell<String> a = new Cell<String>("one");
Cell<Integer> b = new Cell<Integer>(2);
assert a.getId() == 0 && b.getId() == 1 && Cell.getCount() == 2;
静态成员在类的所有实例中共享,因此在分配字符串或整数单元格时,相同的计数会递增。
由于静态成员独立于任何类型参数,因此在访问静态成员时,我们不允许使用类型参数跟随类名称:
Cell.getCount(); // ok
Cell<Integer>.getCount(); // 编译报错
Cell<?>.getCount(); // 编译报错
计数是静态的,所以它是整个类的一个属性,而不是任何特定的实例。
出于同样的原因,您不能在静态成员中的任何地方引用类型参数。 以下是 Cell
的第二个版本,它试图使用一个静态变量来保存存储在任何单元中的所有值的列表:
class Cell2<T> {
private final T value;
private static List<T> values = new ArrayList<T>(); // illegal
public Cell(T value) {
this.value=value;
values.add(value);
}
public T getValue() {
return value;
}
public static List<T> getValues() {
return values;
} // illegal
}
由于类可能在不同的地方使用不同的类型参数,因此在声明静态字段值或静态方法 getValues()
时引用 T
是没有意义的,并且这些行在编译时会报告为错误。如果
我们想要一个单元格中保存的所有值的列表,那么我们需要使用一个对象列表,如下面的变体所示:
class Cell2<T> {
private final T value;
private static List<Object> values = new ArrayList<Object>(); // ok
public Cell(T value) {
this.value=value; values.add(value);
}
public T getValue() {
return value;
}
public static List<Object> getValues() {
return values;
} // ok
}
这段代码编译和运行没有任何困难:
Cell2<String> a = new Cell2<String>("one");
Cell2<Integer> b = new Cell2<Integer>(2);
assert Cell2.getValues().toString().equals("[one, 2]");