java教程——泛型
Posted 我想月薪过万
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java教程——泛型相关的知识,希望对你有一定的参考价值。
我们在前两节讲了 泛型的基本概念 和 泛型的简单使用,这一节我们就来讲讲 泛型的实现原理。
实现原理
泛型 根据语言的不同,有着不同的实现方法。Java语言的泛型实现方式是擦拭法(Type Erasure)。所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。
例如,我们编写了一个泛型类selfMap<T>
,这是编译器看到的代码:
class selfMap<T>{
private T Key;
private T value;
public selfMap(T Key, T value) {
this.Key = Key;
this.value = value;
}
public T getTey() {
return Key;
}
public T getValue() {
return value;
}
}
而虚拟机根本不知道泛型。这是虚拟机执行的代码:
class selfMap{
private Object Key;
private Object value;
public selfMap(Object Key, Object value) {
this.Key = Key;
this.value = value;
}
public Object getTey() {
return Key;
}
public Object getValue() {
return value;
}
}
现在,大家是不是能明白 “擦拭” 的含义了,即 将 T 擦拭为 Object 。
Java使用擦拭法实现泛型,导致了:
- 编译器把类型
<T>
视为Object
; - 编译器根据
<T>
实现安全的强制转型。
使用泛型的时候,我们编写的代码也是编译器看到的代码:
selfMap<String> one = new selfMap<>("name","易齐");
String value = one.getValue();
System.out.println("value ->" + value);
而虚拟机执行的代码并没有泛型:
selfMap one = new selfMap("name","易齐");
String value = (String)one.getValue();
System.out.println("value ->" + value);
所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T
视为Object
处理,但是,在需要转型的时候,编译器会根据T
的类型自动为我们实行安全地强制转型。
局限
局限一:
<T>
不能是基本类型,例如int
,因为实际类型是Object
,Object
类型无法持有基本类型:
局限二:无法取得带泛型的
Class
。观察以下代码:
从打印结果我们可以很直观的看到:无法取得带泛型的Class。
其原因就是我们上面说的,泛型 是在编译器编译的时候实现的,到虚拟机中都是selfMap<Object>,所以,现在是不是明白了为什么打印的是一样的Class实例了。
局限三:无法判断带泛型的类型:
局限四:不能实例化
T
类型:
如果你偏要实例化,也不是不可以,那我们得动用实例化第二种方法了:反射
上述代码借助Class<T>
参数并通过反射来实例化T
类型,使用的时候,也必须传入Class<T>
。例如:
selfMap<String> stringselfMap = new selfMap<>(String.class);
因为传入了Class<String>
的实例,所以我们借助String.class
就可以实例化String
类型。
泛型继承
一个类可以继承自一个泛型类。例如:父类的类型是selfMap<String>
,子类的类型是selfStringMap
,可以这么继承:
class selfStringMap extends selfMap<String>{
public selfStringMap(String key, String value) {
super(key, value);
}
}
使用的时候,因为子类selfStringMap
并没有泛型类型,所以,正常使用即可:
selfStringMap selfStringMap = new selfStringMap("name", "易齐");
前面讲了,我们无法获取selfMap<String>
的T
类型,即给定一个变量selfMap<String> p
,无法从p
中获取到String
类型。
但是,在父类是泛型类型的情况下,编译器就必须把类型T
(对selfStringMap
来说,也就是String
类型)保存到子类的class文件中,不然编译器就不知道selfStringMap
只能存取String
这种类型。
个人理解:因为子类又不是泛型,所以,你不能用檫试法来保存具体类型,即子类只能将具体类型存入自己的class文件中。
在继承了泛型类型的情况下,子类可以获取父类的泛型类型。例如:selfStringMap
可以获取到父类的泛型类型String
。获取父类的泛型类型代码比较复杂:
package test;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
public class changeData {
public static void main(String[] args) {
Class<selfStringMap> selfStringMapClass = selfStringMap.class;
Type t = selfStringMapClass.getGenericSuperclass();
if (t instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType) t;
Type[] types = pt.getActualTypeArguments();
System.out.println(Arrays.toString(types));
}
}
}
class selfStringMap extends selfMap<String>{
public selfStringMap(String key, String value) {
super(key, value);
}
}
class selfMap<T> extends Object{
private T Key;
private T value;
public selfMap(T key,T value) {
this.Key = key;
this.value = value;
}
public T getTey() {
return Key;
}
public T getValue() {
return value;
}
}
因为Java引入了泛型,所以,只用Class
来标识类型已经不够了。实际上,Java的类型系统结构如下:
┌────┐
│Type│
└────┘
▲
│
┌────────────┬────────┴─────────┬───────────────┐
│ │ │ │
┌─────┐┌─────────────────┐┌────────────────┐┌────────────┐
│Class││ParameterizedType││GenericArrayType││WildcardType│
└─────┘└─────────────────┘└────────────────┘└────────────┘
小结
Java的泛型是采用擦拭法实现的;
擦拭法决定了泛型<T>
:
- 不能是基本类型,例如:
int
; - 不能获取带泛型类型的
Class
,例如:Pair<String>.class
; - 不能判断带泛型类型的类型,例如:
x instanceof Pair<String>
; - 不能实例化
T
类型,例如:new T()
。
泛型方法要防止重复定义方法,例如:public boolean equals(T obj)
;
子类可以获取父类的泛型类型<T>
。
下一节:java教程——泛型(四)
以上是关于java教程——泛型的主要内容,如果未能解决你的问题,请参考以下文章