泛型
泛型实现了参数化类型的概念,使代码可以适用于多种类型。
泛型类
T就是类型参数
public class Holder <T>{
private T value;
public Holder(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
泛型接口
public interface Generator <T>{
T generate();
}
泛型方法
能使用泛型方法解决问题,就要避免整个类的泛化。因为它可以使事情更清楚明白。
public class GenericMethods {
public <T> void f(T x){
System.out.println(x.getClass().getName());
}
}
类型擦除
public class GenericHolder<T> {
private T value;
public static void main(String[] args) {
GenericHolder<String> holder = new GenericHolder<String>();
holder.setValue("hello");
String s = holder.getValue();
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
字节码中的类型Object
public void setValue(T);
descriptor: (Ljava/lang/Object;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #8 // Field value:Ljava/lang/Object;
5: return
setValue的时候没有插入类型相关的代码,由编译器确保了类型正确。
最后赋值时,插入了类型类型转换的代码。因为泛型类型被擦除了,实际就是一个Object,需要显示的类型转换。当然这部分转换由编译器帮我们做了。
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class generatic/GenericHolder
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String hello
11: invokevirtual #5 // Method setValue:(Ljava/lang/Object;)V
14: aload_1
15: invokevirtual #6 // Method getValue:()Ljava/lang/Object;
18: checkcast #7 // class java/lang/String
21: astore_2
22: return
类型边界
有时候,我们需要限定类型的范围,就需要用到通配符
List<? extends String> list;
java.util.List<? extends java.lang.String> list;
descriptor: Ljava/util/List;
flags:
Signature: #13 // Ljava/util/List<+Ljava/lang/String;>;
1. 协变:<? extends MyClass>
2. 逆变: <? super MyClass>
3. 无界:<?>
说明:
- T 和?的区别。T是泛化的类型参数,代表着某一种确切的类型。甚至可以这样使用<? extends T>,?是通配符,代表着某一界限下的所有类型。
- 是无界的通配符。似乎和直接声明称Object类型没有啥两样。但是这种显示的声明能够提醒编码和其他协作者,自己是想使用Java泛型来编码的。就像之前写过的RTTI的文章中提到的,声明Class,要比只声明Class好。表示编码者没有忘记类型限定。