Name clash when overriding method of generic class
Asked Answered
F

1

6

I'm trying to understand the name clash error I get with the following code:

import java.util.*;
import javax.swing.*;

class Foo<R extends Number> {
    public void doSomething(Number n, Map<String, JComponent> comps) {
    }
}

class Bar extends Foo {
    public void doSomething(Number n, Map<String, JComponent> comps) {
    }
}

Error message:

error: name clash: doSomething(Number,Map<String,JComponent>) in Bar and doSomething(Number,Map<String,JComponent>) in Foo have the same erasure, yet neither overrides the other

I know I can fix it by either remove the generic type from Foo, or by changing the Bar declaration to class Bar extends Foo<Integer>; what I want to know is why this error occurs in the specific case, but goes away if I remove the comps parameter from each method. I've done some reading about type erasure, but it still appears to me that both methods should have the same erasure with or without generics, and therefore be a valid override in either case. (Note that I haven't even used the generic parameter anywhere yet, which is why I'm so surprised.)

I know that I've added generic types to parent classes before but only got warnings about the subclasses, not errors. Can anyone explain this scenario?

Flavescent answered 1/12, 2014 at 14:47 Comment(8)
You're extending raw Foo.Crispate
@LuiggiMendoza yes, I know that, the question is why Java thinks these methods have different erasures when neither uses the generic parameter, and why it's happy if I remove the comps arguments from both. Possibly something to do with bridge methods, but I can't see it.Flavescent
Map interface use generics, and maybe that's why it's complaining. Which specific version of Java are you using? By specific, I mean java 6 u30, for example, or Java 7 u40.Crispate
@LuiggiMendoza I'm using Eclipse Luna with JDK1.7.0_55, also tested on ideone.com, not sure which version they're using.Flavescent
Be aware that Eclipse is using an internal (not JDK) compiler that used to have problems with generics. Not sure if that is still the case. Did you try to compile it with a plain javac yet?Lindbom
It's also happy if I change both methods to use a raw Map; not sure what that tells us.Flavescent
@Lindbom I'd seen some posts about that, but Eclipse doesn't seem to be at fault here. javac is giving me the same resultsFlavescent
@BradMace Netbeans is happy if I change only the second method to use a raw Map.Midget
C
7

Luiggi is right in the comments. This is a consequence of raw types.

The supertype of a class may be a raw type. Member accesses for the class are treated as normal, and member accesses for the supertype are treated as for raw types. In the constructor of the class, calls to super are treated as method calls on a raw type.

This applies when invoking a supertype method, but also when overriding one.

Take for example, the following

class Bar extends Foo {
    public Bar() {
        doSomething(1, new HashMap<Number, String>());
    }
}

You'll notice that it compiles, even though HashMap<Number, String> is not a type that is assignable to Map<String, JComponent>.

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

(Note that C in our case is Bar.)

And the same thing happens when trying to override a method. When trying to override the Foo#doSomething(..) method, your Bar class is actually seeing it declared as

public void doSomething(Number n, Map comps) {
}

In other words, every usage of type parameters is erased. So attempting to declare the method

public void doSomething(Number n, Map<String, JComponent> comps) {
}

in the subtype Bar is actually an attempt at overloading, not overriding. And this fails because of type erasure. The proper override, which you can verify with @Override, is

public void doSomething(Number n, Map comps) {
}

Further reading:

Chaldron answered 1/12, 2014 at 15:40 Comment(2)
Excellent explanation, particularly about overloading vs overriding. (The clues were in the error message, but I was so surprised that I missed them.) To distill your explanation down, if a subclass fails to specify a class-level generic parameter, even completely unrelated generic parameters in the parent class will effectively cease to exist from the subclass's perspective?Flavescent
@brad Everything except for generic usage in static methods.Chaldron

© 2022 - 2024 — McMap. All rights reserved.