从JDK6开始类型参数的值(泛型)算入方法签名(method signature)

本文探讨了Java SE 5.0和6.0中关于类型参数(泛型)如何影响方法签名的规范。根据JLS,相同类名和类型参数的引用类型被认为是相同的编译时类型。在JDK6之前,类型参数的值未被纳入方法签名的考量,导致某些情况下的编译错误。JDK6修复了这个问题,允许泛型版本的方法成为非泛型版本的子签名。文章通过示例代码和JDK bug 6182950说明了这一变化,并指出某些IDE可能在语法检查时仍存在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaSE5.0, JavaSE6.0在语法上都是遵守最新的The Java Language Specification(Third Edition)。

 

在JLS的”4.3.4 When Reference Types Are the Same“节中有如下描述:

    Two reference types are the same compile-time type if they have the same binary
    name (§13.1) and their type parameters, if any, are the same, applying this definition
    recursively

就是说: 如同两个引用类型具有相同类名和类型参数的值,那么他们是相同的编译时类型,即使运行时他们对应同一个Class对象。

 

在JLS的”8.4.2 Method Signature“节中,其描述意思如下:

存在方法签名m1和m2; 等号是包括的意思

1.  方法签名 = 方法名 + 参数类型(argument types)

2.  参数类型(argument types) = 形式参数(formal parameters)+ 形式类型参数 (formal type parameters) 

3.  形式参数(formal parameters)= 参数的类型 + 参数的个数

4.  形式类型参数 (formal type parameters)= 类型参数的个数

5.  子签名:如果m1与m2相同或者m1与m2的擦除签名相同,那么m1是m2的子签名。

6.  为了类库的作者考虑,规定方法的任何泛型版本都是非泛型版本的子签名.

7.  覆写相等:如果m1是m2的子签名或者m2是m1的子签名,那么m1和m2覆写相等。

8.  如果两个方法的签名是覆写相等的那么报编译时错误。

 

这段看起来让人感觉不是很好,他从整个方法签名的角度去描述两个泛型方法(方法参数带类型参数的值)的签名是否冲突,为此还引入了签名的擦除、覆写相等等概念。我认为比较好的做法是把对泛型的处理转移到引用类型上,即将泛型加入到判断引用类型是否相同的逻辑上,这样就得到下图

 

这样,判断方法签名还是按照以前的传统方式,使用方法的方法名+参数类型+参数个数,而不需要引入“签名的擦除”、“覆写相等”等概念。

感觉JDK6的javac在编译时进行语法检查就是按照这样的方式的进行的。

 

结合JLS的4.3.4节和8.4.2节,可以得出类型参数的值算入引用类型,当形式参数的参数类型是引用类型时,类型参数的值算入方法签名。

 

但是,在对JLS的理解上JavaSE5.0有问题,没有将类型参数的值算入method signature,所以存在JDKbug 6182950 (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950),下面是其描述

 

FULL PRODUCT VERSION :
    java version "1.5.0"
    Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
    Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :

    customer  Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
    Following code should not compile:
    class x {
            int f(List<String> l) {return 0;}
            double f(List<Integer> l) {return 0;}
    }

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Following code should not compile:
    class x {
            int f(List<String> l) {return 0;}
            double f(List<Integer> l) {return 0;}
    }

since both method have the same erasure.
JLS3 draft 8.4.8.3 says, it is error if there are two methods m1 and m2 in a class such that:
- m1 and m2 have the same name
- m2 is accessible
- the signature of m1 is not subsignature if m2
- both methods have same erasure

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
    Error:  line (4) name clash: f(java.util.List<java.lang.String>) and f(java.util.List<java.lang.Integer>) have the same erasure

 

大意是说类

    class x {
            int f(List<String> l) {return 0;}
            double f(List<Integer> l) {return 0;}
    }

应该不能通过编译,原因是两个方法具有相同的擦除(难道本意是除去类型参数的值后,两个方法是相同的?)。

 

用jdk1.5.0_22编译此类报错如下

名称冲突:f(java.util.List<java.lang.Integer>) 和 f(java.util.List<java.lang.String>) 具有相同疑符。

但是JavaSE6.0修正了这个问题。使用JDK1.6.0_17能成功编译此类。然而一些IDE在做语法检查时也有此问题,所以他们会提示语法错误,但是使用javac可以通过编译。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值