所以,我正在尝试编写一个方法来回答我之前的一个问题:如何判断一个任意的java.lang.Method是否会覆盖另一个?为此,我正在阅读JLS,并且在一个案例中似乎缺少一些部分.
想象一下,您有以下课程:
public class A<T> {
public void foo(T param){};
}
public class B extends A<String> {
public void foo(String param){};
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,很明显是B.foo
覆盖A.foo
,但我不明白这种情况如何符合规范.
关于方法覆盖,JLS§8.4.8.1规定:
在类C中声明的实例方法m1将覆盖在类A中声明的另一个实例方法m2,如果以下所有条件都为真:
C是A的子类.
m1的签名是m2签名的子签名(§8.4.2).
或者:
- m2在与C相同的包中是公共的,受保护的或声明的,具有默认访问权限
- m1覆盖方法m3(m3与m1不同,m3与m2不同),使得m3覆盖m2.
显然,在我们的案例中,第1点和第3点是满意的.让我们在JLS中看一下子签名意味着什么.该JLS§8.4.2说:
如果两个方法具有相同的名称和参数类型,则它们具有相同的签名.
如果满足以下所有条件,则两个方法或构造函数声明M和N具有相同的参数类型:
它们具有相同数量的形式参数(可能为零)
它们具有相同数量的类型参数(可能为零)
设A1,...,An为M的类型参数,让B1,...,Bn为N的类型参数.将N的类型中每次出现的Bi重命名为Ai后,相应类型变量的界限为同样,M和N的形式参数类型是相同的.
在我们的例子中,第1点显然是正确的(都有1个参数).
第2点有点混乱(这就是我不确定规范究竟意味着什么):两种方法都没有声明它们自己的类型参数,而是A.foo
使用T
哪种类型变量来对类进行参数化.
所以我的第一个问题是:在这个上下文中,是否在类计数中声明类型变量?
好的,现在让我们假设T
不计算,因此第2点是假的(我不知道在这种情况下我怎么能应用第3点).我们的两种方法没有相同的签名,但这并不妨碍B.foo
成为其子签名A.foo
.
在JLS§8.4.2中稍微进一步说:
方法m1的签名是方法m2的签名的子签名,如果:
m2与m1具有相同的签名,或
m1的签名与m2签名的擦除(§4.6)相同.
我们已经确定第1点是错误的.
根据JLS§4.6的方法的擦除签名是a signature consisting of the same name as s and the erasures of all the formal parameter types given in s
.所以A.foo的删除和B.foo foo(Object)
的删除是foo(String)
.这两个是不同的签名,因此第2点也是假的,而B.foo不是A.foo的子签名,因此B.foo不会覆盖A.foo.
除了它......
我错过了什么?是否有一些我没有看到的难题,或者在这种情况下规范真的不完整?
在类中声明的类型变量是否计数?
有问题的元素是方法,而不是包含的类型声明。所以,不,他们不算数。
好的,现在让我们假设 T 不算数,因此第 2 点是错误的
为什么?它们都有 0 个类型参数,所以这是真的。
一步步:
- 它们具有相同数量的形式参数(可能为零)
这两种方法都有 1 个形式参数。查看。
- 它们具有相同数量的类型参数(可能为零)
这两种方法都没有声明类型参数。查看。
设 A1, ..., An 为 M 的类型参数, B1, ..., Bn 为 N 的类型参数。 将 N 类型中每次出现的 Bi 重命名为 Ai 后,对应类型变量的边界为相同,且 M 和 N 的形参类型相同。
由于这两种方法都没有声明类型参数,因此无需重命名并且形式参数类型相同 -- T[T=String] = String
。查看。
? B.foo(String)
与 具有相同的签名A<String>.foo(String)
。? B.foo(String)
是一个子签名A<String>.foo(String)
由于B
是的子类A
并且A<String>.foo(String)
是公共的,我们可以得出 B.foo(String)
overrides 的结论A<String>.foo(String)
。