JLS完全重新定义方法是否覆盖和泛型?

Lor*_*igs 5 java generics jls

所以,我正在尝试编写一个方法来回答我之前的一个问题:如何判断一个任意的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,如果以下所有条件都为真:

  1. C是A的子类.

  2. m1的签名是m2签名的子签名(§8.4.2).

  3. 或者:

    • m2在与C相同的包中是公共的,受保护的或声明的,具有默认访问权限
    • m1覆盖方法m3(m3与m1不同,m3与m2不同),使得m3覆盖m2.

显然,在我们的案例中,第1点和第3点是满意的.让我们在JLS中看一下子签名意味着什么.该JLS§8.4.2说:

如果两个方法具有相同的名称和参数类型,则它们具有相同的签名.

如果满足以下所有条件,则两个方法或构造函数声明M和N具有相同的参数类型:

  1. 它们具有相同数量的形式参数(可能为零)

  2. 它们具有相同数量的类型参数(可能为零)

  3. 设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的签名的子签名,如果:

  1. m2与m1具有相同的签名,或

  2. 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.

除了它......

我错过了什么?是否有一些我没有看到的难题,或者在这种情况下规范真的不完整?

Ben*_*ulz 5

在类中声明的类型变量是否计数?

有问题的元素是方法,而不是包含的类型声明。所以,不,他们不算数。

好的,现在让我们假设 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)

  • 这里的关键步骤是`B`扩展了`A&lt;String&gt;`而不是`A&lt;T&gt;`,因此基类中方法的签名应该被理解为`foo(String)`而不是` foo(T)` 或 `foo(Object)`。我现在明白了,谢谢。 (3认同)