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

2

7

I needed to dig into the specifics of method invocation in Java, and while reading the section Choosing the Most Specific Method in The Java Language Specification (Java SE 12 Edition), I found that (1) during invocation multiple methods can be maximally specific and that (2) having multiple maximally specific methods doesn't always result in a compile-time error.

I was able to think of an example where two methods are both maximally specific:

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);
    }
}

This example results in a compile-time error: error: reference to foo is ambiguous

This makes sense to me, but what doesn't make sense to me is when there are multiple maximally specific methods and it doesn't result in a compile-time error.

The section Choosing the Most Specific Method in The Java Language Specification (Java SE 12 Edition) mentions two scenarios where the compiler is able to select a method when there are multiple maximally specific methods:

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

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

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

Skiing answered 7/9, 2019 at 18:40 Comment(3)
"what doesn't make sense to me is when multiple methods have the same signature and it doesn't result in a compile-time error" you don't have multiple methods with the same signature here: the type parameters are part of the signature, and they have different bounds. So it's not really clear what you mean.Parous
After erasure, the methods have the signature ` void foo(A t) {};` or ` void foo(B t) {};` respectively.Diastema
Even if the methods don't have the same signature, they are both still maximally specific methods and thus, my question still remains. I will update my question to avoid this confusion.Skiing
G
2

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
    }
}
Grenade answered 7/9, 2019 at 20:13 Comment(1)
Thank you for the response! I believe you are correct about the methods that I referred to as having different signatures and thus, I edited my answer to reflect that. However, my question still remains: when is a compiler able to successfully invoke a method when there are multiple maximally specific methods? And I think you have answered this as well, but I want to give it some more thought first before I accept it.Skiing
K
1

With regard to the first bullet, here is one possible 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

However, making one of the m methods abstract eliminates the error.

Kythera answered 7/9, 2019 at 21:16 Comment(1)
If I make both methods abstract, then I get the original error, which seems to go against the second bullet from the spec in my question: "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."Skiing

© 2022 - 2024 — McMap. All rights reserved.