泛型及类型擦除
泛型是什么?
泛型的英文是 generics,generic的意思是通用的。还有一种较为准确的说法就是为了参数化类型,或者说可以将类型当作参数传递给一个类或者是方法
在Java1.5之前,没有泛型的时候,我们也可以存取任何值,只要我们做到正确的类型转换即可
public class Fanxing {
public static void main(String[] args) {
StoreEverything se = new StoreEverything();
se.setValue(1999);
int r = (int) se.getValue();
System.out.println(r);
se.setValue("StringValue");
String str = (String) se.getValue();
System.out.println(str);
}
}
class StoreEverything {
Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
有了泛型之后,我们可以这样写
public class Fanxing {
public static void main(String[] args) {
StoreEverything<Integer> se = new StoreEverything<>();
se.setValue(1999);
Integer value = se.getValue();
System.out.println(value);
}
}
class StoreEverything<T> {
T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
这就是泛型,它将 value 这个属性的类型也参数化了,这就是所谓的参数化类型
泛型的定义和使用
泛型按照使用情况可以分为 3 种
-
泛型类
-
泛型方法
-
泛型接口
与开头的例子类似,我们可以这样定义一个泛型类:
public class Test<T> {
T field;
}
尖括号<>中的 T 被称作是类型参数,用于指代任何类型。T只是我们约定俗成的写法,没有强制要求。但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如:
- T 代表一般的任何类
- E 代表 Element 的意思,或者 Exception 异常的意思
- K 代表 Key 的意思(常见于Map中)
- V 代表 Value 的意思,通常与 K 一起配合使用
- S 代表 Subtype 的意思
如果一个类被<T>的形式定义,那么它就被称为是泛型类
泛型类使用
Test<String> test1 = new Test<>();
Test<Integer> test2 = new Test<>();
泛型类不至接受一个类型参数,它还可以这样接受多个类型参数
泛型方法
泛型方法与泛型类稍有不同的地方是,类型参数也就是尖括号那一部分是写在返回值前面的。<T>中的 T 被称为类型参数,而方法中的 T 被称为参数化类型,它不是运行时真正的参数
public <T> T testMethod1(T t){
return null;
}
泛型接口
泛型接口和泛型类差不多
public interface Iterable<T> {
}
通配符 ?
除了用 <T>表示泛型外,还有<?>这种形式。? 被称为通配符
通配符的出现是为了指定泛型中的类型范围
通配符有 3 种形式:
<?>被称作无限定的通配符
<? extends T>被称作有上限的通配符
<? super T>被称作有下限的通配符
无限定通配符 <?>
无限定通配符经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关
类型擦除
泛型是 Java 1.5 版本才引进的概念,在这之前是没有泛型的概念的,但显然,泛型代码能够很好地和之前版本的代码很好地兼容。
这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
通俗地讲,泛型类和普通类在 java 虚拟机内是没有什么特别的地方
例如代码:
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
返回的结果是true
打印的结果为 true 是因为 List<String>和 List<Integer>在JVM中的 Class 都是 List.class
泛型信息被擦除了
public class Erase<T> {
T object;
public Erase(T object) {
this.object = object;
}
}
Erase 是一个泛型类,我们查看它在运行时的状态信息可以通过反射
Erase<Integer> erase = new Erase<>(1999);
Class aClass = erase.getClass();
System.out.println("Erase class is: " + aClass.getName());
结果是
Erase class is: Apr26.Erase
Apr26 为包名
Class 的类型仍然是Erase并不是Erase
Field[] fs = aClass.getDeclaredFields();
for ( Field f:fs) {
System.out.println("Field name "+f.getName()+" type:"+f.getType().getName());
}
结果还是Object类型
Field name object type:java.lang.Object
也许你会以为泛型类被类型擦除后,相应的类型就被替换成 Object 类型了,其实不然,如下面这段代码所示:
public class Erase<T extends String> {
T object;
public Erase(T object) {
this.object = object;
}
public static void main(String[] args) {
Erase<String> erase = new Erase<>("1999");
Class aClass = erase.getClass();
System.out.println("Erase class is: " + aClass.getName());
Field[] fs = aClass.getDeclaredFields();
for (Field f : fs) {
System.out.println("Field name " + f.getName() + " type:" + f.getType().getName());
}
}
}
结果为
Erase class is: Apr26.Erase
Field name object type:java.lang.String
由此我们的结论是:泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如<T>则会被转译成普通的 Object 类型,如果指定了上限如 <T extends String>则类型参数就被替换成类型上限
泛型中值得注意的地方
- 泛型类或者泛型方法中,不接受 8 种基本数据类型
- Java 不能创建具体类型的泛型数组

浙公网安备 33010602011771号