Example of multiple maximally specific methods that does not result in a compile-time error
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.
A class can't contain two methods with the same signature.
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.
In your example, there are two methods with two different signatures. It compiles and works fine unless you introduce ambiguity like new C().<C>foo(null);
. The compile-time error "reference to foo is ambiguous" doesn't mean <T extends A> void foo(T t)
and <T extends B> void foo(T t)
can't coexist. They actually can, and do.
As mentioned in the comments, after type erasure, the methods will look like
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?
Invoking an abstract method within an abstract context (e.g. inside an abstract class) is absolutely fine.
Can someone provide an example for each of these two scenarios that don't result in compile-time errors?
I can think of an example where there are two "maximally specific methods with override-equivalent signatures" for the invocation new C().foo();
and it's being successfully resolved in favour of A
's method.
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
}
}