Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 437
johnnyodonnell Avatar asked Sep 07 '19 18:09

johnnyodonnell


People also ask

What are the most common compile time errors in Java?

The most common compile time errors. Incorrect naming of the Java file – The name of the Java file in which you write you’re Java code must exactly match the name of the public class contained within the associated Java file. So, if your code contains a public class named Game, the associated Java file must be named Game.java, not game.java,...

What are the differences between compile-time and run-time errors?

The Differences between Compile-Time and Run-Time Error are: Compile-Time Errors. Runtime-Errors. These are the syntax errors which are detected by the compiler. These are the errors which are not detected by the compiler and produce wrong results.

Why does my Java code fail to compile?

Improper casing of code – Java is case sensitive, so public is different from Public which is different from puBliC. Java newbies tend to capitalize letters that should be lower case, and vice-versa, and as a result of improper casing, the code they write fails to compile.

What are the most common compile-time errors?

Most frequent Compile-Time errors are: 1 Missing Parenthesis ( }) 2 Printing the value of variable without declaring it 3 Missing semicolon (terminator) More ...


1 Answers

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
    }
}
like image 128
Andrew Tobilko Avatar answered Oct 08 '22 00:10

Andrew Tobilko