您的位置:首页 > 编程语言 > Java开发

java语法糖 泛型与类型擦除

2016-09-26 22:49 183 查看
学习 周志明《深入理解java虚拟机》

若把java程序中的信息分为“代码”和“元数据”,那么在class文件中,code属性用于描述代码,所有其它项目都用于描述元数据。

1. 代码:code,方法体里面的java代码。

2. 元数据:MetaData,包括类、接口、方法定义以及其它信息。

在java语言中,要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须有一个与原方法不同的特征签名,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,因为返回值不会包含在特征签名中,因此无法仅仅依靠返回值不同来对方法进行重载(返回值与参数不同时,是可以重载的)。但是在class文件中,特征签名的范围更大一些,只要描述符不是完全一致的,两个方法也可以共存。也就是,如果两个方法有相同的名称和特征签名,但返回值不同,也是可以合法共存于同一个class文件中。

1. 特征签名是一个方法中各个参数在常量池中的字段符号引用的集合。

2. 描述符是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。

任何类、接口、初始化方法和成员的泛型签名,如果包含了类型变量(TypeVariables)或者参数化类型(Parameterized Types),则特征签名属性会为它记录泛型签名信息。

看段代码,示例一:

public class GenericTypes{

public static void method(List<String> list){
System.out.println("invoke method(List<String> list)");
}

public static void method(List<Integer> list){
System.out.println("invoke method(List<Integer> list)");
}
}

这段代码是无法被编译通过的,因为泛型参数List<String>和List<Integer>编译之后都被擦除了,成了原生类型List。初步看来,无法重载的原因找到了,只能说,泛型擦除成了相同的原生类型只是无法重载的一部分原因。

再来段代码,示例二:

public class GenericTypes{

public static String method(List<String> list){
System.out.println("invoke method(List<String> list)");
return "";
}

public static int method(List<Integer> list){
System.out.println("invoke method(List<Integer> list)");
return 1;
}

public static void main(String[] args){
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
}
执行结果

invoke method(List<String> list)
invoke method(List<Integer> list


上述代码,两个method方法中添加了不同的返回值,重载居然成功了,即这段代码可以编译和执行。这是对java语言中返回值不参数重载选择的基本认知挑战吗。

为什么呢?是因为,方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名中,所以返回值不参与重载选择,但是在class文件中,只要描述符不是完全一致的方法就可以共存。也就是说,两个方法如果有相同的名称和特征签名,但返回值不同,那他们也是可以合法共存于一个class文件中的。

因为两个方法可以共存于同一个class文件,代码可以编译通过。又因为特征签名保存了参数化类型信息(即泛型信息),在执行的时候,可以通过反射手段取得参数化类型信息,因此示例二可以编译和执行。

由于java泛型的引入,各种场景(虚拟机解析、反射等)下的方法调用都可能对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等。虚拟机规范因进入了Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题,Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型信息。

另外,从signature属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: