35

I have superclass Foo. And a class Bar extending it.

public class Bar extends Foo

Function in Foo:

protected void saveAll(Collection<?> many)

Function in Bar:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

Getting error :

 Name clash: The method saveAll(Collection<MyClass>) of type Bar has the same erasure as saveAll(Collection<?>) of type Foo but does not override it.

What am I doing wrong?

Mathias Schwarz
  • 7,099
  • 23
  • 28
Jaanus
  • 16,161
  • 49
  • 147
  • 202

6 Answers6

24

You are overriding the saveAll method with an incompatible type. Perhaps you want to do something like:

public class Bar extends Foo<MyClass>

Function in Foo<E>

protected void saveAll(Collection<E> many)

and function in Bar:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}
Mathias Schwarz
  • 7,099
  • 23
  • 28
8

Due to the type erasure feature of Java, the JVM will not be able to know whether it is the method that has the parametrized type MyClass or the first one that should be called.

If possible or applicable, the most commonly used pattern I've seen to avoid this is to change the class Foo to have a parametrized type as well:

public class Foo<T> {
    protected void saveAll(Collection<T> many) {}
}

and then have Bar simply implement Foo for your specific type:

public class Bar extends Foo<MyClass> {
    public void saveAll(Collection<MyClass> many) {
        super.saveAll(many);
    }
}
Joel Westberg
  • 2,656
  • 1
  • 21
  • 27
  • Note: This doesn't help for Foo is an interface, if an implementing class Bar might (bad designed) implement `Foo` and `Foo` where you ran again in the same type erasure since generic parameter is erased to `Collection` again. "Better" use `T` as Parameter – childno͡.de Mar 05 '18 at 12:03
5

At runtime, the parameter types are replaced by Object. So saveAll(Collection<?>) and saveAll(Collection<MyClass>) are transformed to saveAll(Collection). This is a name clash. Look here for details.

You could do this :

public class Foo<T> {
    protected void saveAll(Collection many) {
        // do stuff
    }
}

public class Bar extends Foo<MyClass> {
}
gontard
  • 28,720
  • 11
  • 94
  • 117
2

When the compilers compiles to byte code a process called Erasure happens. This remove the type information from the collections. I believe it will manually do the casts etc as part of the process of generating the byte code. If you remove the generic parts of your class (ie the <..> ) then you will see you have two saveAll methods. The error is that you have two save all methods will the same signature. The collections have type object in the byte code.

Try removing the <..> which might make it clearer. When you put the <...> back in then consider the name of the methods. If they are different it should compile.

Also I dont think this is a hibernate problem so this tag should be removed. It is a java generic problem you have.

What you could do here is type the class

public class Bar extends Foo<MyClass>

and then have the method types to T

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

and then the declaration of Foo would be something like

public abstract class Bar extends Foo<T> {
    public void saveAll(Collection<T> stuff) {
}
RNJ
  • 15,272
  • 18
  • 86
  • 131
1

You just overriding methods with different Signatures.

What will be good idea is to use PECS (Producer - Extends, Consumer - Super) rule described in Effective Java Second Edition by Joshua Bloch.

according to this rule it should looks like this.

In Foo class:

public class Foo<E>{

protected void saveAll(Collection<? super E> many){....}

protected void getAll(Collection<? extends E> many){....}

}
John Conde
  • 217,595
  • 99
  • 455
  • 496
danny.lesnik
  • 18,479
  • 29
  • 135
  • 200
  • Actually the saveAll method will probably iterate over the list and save each element, and thus treat the list as a producer. In that case it needs to be defined as `protected void saveAll(Collection extends E> many){....} ` – herman Aug 13 '12 at 13:23
0

While the existing answers are correct, this error easily occurs when you declare the actual type not in the "extends ..." clause but in the actual class name:

Wrong:

public class Bar<MyClass> extends Foo
{
    ...
}

Correct:

public class Bar extends Foo<MyClass>
{
    ...
}
MRalwasser
  • 15,605
  • 15
  • 101
  • 147