Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics in overridden methods

Ran into an interesting issue; the following class compiles:

public class Test {

    public static void main(String[] args) throws Exception {
        A a = new A();
        B b = new B();

        foo(a);
        foo(b);
    }   

    private static void foo(A a) {
        System.out.println("In A");
    }   

    private static void foo(B b) {
        System.out.println("In B");
    }   

    private static class A {}

    private static class B extends A {}

}

but this one fails:

public class Test {

    public static void main(String[] args) throws Exception {
        A<String> a = new A<>();
        B b = new B();

        foo(a);
        foo(b);
    }   

    private static void foo(A<String> a) {
        System.out.println("In A");
    }   

    private static void foo(B b) {
        System.out.println("In B");
    }   

    private static class A<T> {}

    private static class B extends A {}

}

with this error:

Test.java:8: error: reference to foo is ambiguous, both method foo(A<String>) in Test and method foo(B) in Test match              
        foo(b);                                                                                                                    
        ^                                                                                                                          
Note: Test.java uses unchecked or unsafe operations.                                                                               
Note: Recompile with -Xlint:unchecked for details.                                                                                 
1 error

I'd have thought that due to type erasure that these would be essentially identical. Any one know what's going on here?

like image 413
Kiersten Arnold Avatar asked Apr 11 '13 19:04

Kiersten Arnold


People also ask

How do you override generic methods in Java?

A method in a generic class can be overridden like any other method. The output is shown here: The overridden version of getValue() is called for objects of type MyClass2, but the superclass version is called for objects of type MyClass.

What are the generic methods?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

What is the disadvantages of using generics?

According to oracle documentation, the following points are the disadvantage of generics: Cannot instantiate Generic types with primitive types. Cannot create instances of type parameters. Cannot declare static fields whose types are type parameters.


2 Answers

The cause is that you are mixing generics and raw types (B should be declared like class B<T> extends A<T> or class B extends A<SomeType>).

The actual reason why this is happening is buried somewhere in the JLS, section #15.12.2.7 and following - good luck to articulate it succintly ;-)

like image 175
assylias Avatar answered Sep 26 '22 00:09

assylias


Before the dawn of generics, Java had methods like

public class Collections

    public void sort(List list) {...}               [1]

And user code may have things like

public class MyList implements List ...             [2]

MyList myList = ...;
Collections.sort(myList);                           [3]

When generics was added to Java, a decision was made to convert existing classes and methods to generic ones, without breaking any code using them. That is a great achievement in terms of difficulty, at the great price of leaving the language complicated and flawed.

So [1] was generified, but [3] must still compile as is, without having to generify [2].

The hack is in §15.12.2.3

Ai can be converted by method invocation conversion (§5.3) to Si

basically saying that if the argument type (Ai) is raw, then erase the parameter type (Si) too for the purpose of matching.

Back to your example, we see why foo(A<String>) is considered applicable for foo(b).

However there's another question - is foo(A<String>) applicable per [§15.12.2.2]? The answer seems to be "no" by the letter of the spec. But it may be a bug of the spec.

like image 28
ZhongYu Avatar answered Sep 23 '22 00:09

ZhongYu