Omry Yadan
Omry Yadan

Reputation: 33596

Method has the same erasure as another method in type

Why is it not legal to have the following two methods in the same class?

class Test{
   void add(Set<Integer> ii){}
   void add(Set<String> ss){}
}

I get the compilation error

Method add(Set) has the same erasure add(Set) as another method in type Test.

while I can work around it, I was wondering why javac doesn't like this.

I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single

public void add(Set<?> set){}

method, but this is not always the case.

This is extra annoying if you want to have two constructors that takes those arguments because then you can't just change the name of one of the constructors.

Upvotes: 439

Views: 242556

Answers (9)

Homail ali
Homail ali

Reputation: 1

I think it is because of type erasure since after compilation both the Integer and String will be replaced with Object hence resulting in two functions with the same parameters which is against method overloading

Upvotes: 0

Piotr Rogowski
Piotr Rogowski

Reputation: 3880

In this case can use this structure:

class Test{
   void add(Integer ... ii){}
   void add(String ... ss){}
}

and inside methods can create target collections

void add(Integer ... values){
   this.values = Arrays.asList(values);
}

Upvotes: 0

Kote Isaev
Kote Isaev

Reputation: 355

I bumped into this when tried to write something like: Continuable<T> callAsync(Callable<T> code) {....} and Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...} They become for compiler the 2 definitions of Continuable<> callAsync(Callable<> veryAsyncCode) {...}

The type erasure literally means erasing of type arguments information from generics. This is VERY annoying, but this is a limitation that will be with Java for while. For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example. Or use initialization methods instead... (virtual constructors?) with different names...

for similar operation methods renaming would help, like

class Test{
   void addIntegers(Set<Integer> ii){}
   void addStrings(Set<String> ss){}
}

Or with some more descriptive names, self-documenting for oyu cases, like addNames and addIndexes or such.

Upvotes: 3

erickson
erickson

Reputation: 269627

This rule is intended to avoid conflicts in legacy code that still uses raw types.

Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:

class CollectionConverter {
  List toList(Collection c) {...}
}

You extend my class, like this:

class Overrider extends CollectionConverter{
  List toList(Collection c) {...}
}

After the introduction of generics, I decided to update my library.

class CollectionConverter {
  <T> List<T> toList(Collection<T> c) {...}
}

You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.

Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:

class Overrider extends CollectionConverter {
  @Override
  List toList(Collection c) {...}
  @Override
  <T> List<T> toList(Collection<T> c) {...}
}

Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.

The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.

Upvotes: 400

Idrees Ashraf
Idrees Ashraf

Reputation: 1383

Define a single Method without type like void add(Set ii){}

You can mention the type while calling the method based on your choice. It will work for any type of set.

Upvotes: 8

bruno conde
bruno conde

Reputation: 48255

This is because Java Generics are implemented with Type Erasure.

Your methods would be translated, at compile time, to something like:

Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)

void add(Set ii);
void add(Set ss);

Both methods have the same signature without the type parameters, hence the error.

Upvotes: 52

kgiannakakis
kgiannakakis

Reputation: 104168

The problem is that Set<Integer> and Set<String> are actually treated as a Set from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String> and Set<Integer>.

Upvotes: 28

rossoft
rossoft

Reputation: 2182

It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.

Upvotes: 3

GaryF
GaryF

Reputation: 24330

Java generics uses type erasure. The bit in the angle brackets (<Integer> and <String>) gets removed, so you'd end up with two methods that have an identical signature (the add(Set) you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.

If Java ever gets reified generics, then you could do this, but that's probably unlikely now.

Upvotes: 139

Related Questions