不会导致编译时错误的多个最大特定方法的示例

Example of multiple maximally specific methods that does not result in a compile-time error

我需要深入研究 Java 中方法调用的细节,并且在阅读 Java 语言规范中的 Choosing the Most Specific Method 部分时(Java SE 12 Edition),我发现 (1) 在调用期间多个方法可以最大程度地特定并且 (2) 拥有多个最具体的方法并不总是会导致编译时错误。

我想到了一个例子,其中两种方法都是最具体的:

interface A {}

interface B {}

class C implements A, B {
    <T extends A> void foo(T t) {};
    <T extends B> void foo(T t) {};
}

class Main {
    public static void main(String[] args) {
        new C().<C>foo(null);
    }
}

此示例导致编译时错误:error: reference to foo is ambiguous

这对我来说有意义,但对我来说没有意义的是当有多个最具体的方法并且它不会导致编译时错误.

Java 语言规范(Java SE 12 版) 中的 Choosing the Most Specific Method 部分提到了编译器能够select 有多个最大特定方法时的方法:

  • If all the maximally specific methods have override-equivalent signatures (§8.4.2), and exactly one of the maximally specific methods is concrete (that is, neither abstract nor default), then it is the most specific method.

  • Otherwise, if all the maximally specific methods have override-equivalent signatures, and all the maximally specific methods are abstract or default, and the declarations of these methods have the same erased parameter types, and at least one maximally specific method is preferred according to the rules below, then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that are preferred. The most specific method is then considered to be abstract.

首先,如何调用 abstract 的方法?为什么 abstract 方法会被考虑用于方法调用?

其次,有人可以为这两种情况中的每一种提供一个不会导致编译时错误的示例吗?

I found that (1) during invocation multiple methods can have the same signature and that (2) having multiple methods with the same signature doesn't always result in a compile-time error.

一个 class 不能包含两个具有相同签名的方法。

8.4.2. Method Signature

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or

  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

It is a compile-time error to declare two methods with override-equivalent signatures in a class.

在您的示例中,有两个方法具有两个不同的签名。它编译并工作正常,除非你引入像 new C().<C>foo(null); 这样的歧义。编译时错误 "reference to foo is ambiguous" 并不意味着 <T extends A> void foo(T t)<T extends B> void foo(T t) 不能共存。他们确实可以,而且确实做到了。

如评论中所述,类型擦除后,方法将如下所示

 void foo(A t);
 void foo(B t);

How is it possible to invoke a method that is abstract? Why would an abstract method ever be considered for method invocation?

在抽象上下文中调用抽象方法(例如在抽象中 class)绝对没问题。

Can someone provide an example for each of these two scenarios that don't result in compile-time errors?

我可以想到一个示例,其中有两个 "maximally specific methods with override-equivalent signatures" 用于调用 new C().foo();,并且它已成功解析为 A 的方法。

abstract class A {
    public void foo() {
        System.out.println("a");
    }
}
interface B {
    default void foo() {
        System.out.println("b");
    }
}
class C extends A implements B {
    public static void main(String[] args) {
        new C().foo();  // prints a
    }
}

关于第一个项目符号,这是一个可能的 example:

abstract class Foo<T> {
    void m(Integer param) {}
    void m(T param) {}
}

Foo<Integer> foo = null;
Integer param = 0;
foo.m(param); // Error: reference to m is ambiguous

但是,使用 m 方法之一 abstract 可以消除错误。