Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type Erasure and Overloading in Java: Why does this work?

I have the following code:

public class Pair< T, U > {
    public T first;
    public U second;
}
public class Test {
    public int method( Pair< Integer, Integer > pair ) {
        return 0;
    }
    public double method( Pair< Double, Double > pair ) {
        return 1.0;
    }
}

This actually compiles and works like one would expect. But if the return types are made to be the same, this doesn't compile, with the expected "name clash: method(Pair) and method(Pair) have the same erasure"

Given that the return type isn't part of the method signature, how is this overloading possible?

like image 506
Kyle Dewey Avatar asked Apr 03 '11 02:04

Kyle Dewey


3 Answers

Consider the following 4 methods

           Java code                        bytecode

m1:    Byte f(List<Byte> list)           f List -> Byte
m2:    Long f(List<Byte> list)           f List -> Long
m3:    Byte f(List<Long> list)           f List -> Byte
m4:    Long f(List<Long> list)           f List -> Long

According to the current Java Language Spec,

  • m1 and m2 cannot coexist, nor can m3 and m4. because they have the same parameter types.

  • m1 and m3 can coexist, so can m1 and m4. because they have different parameter types.

But javac 6 only allows m1+m4, not m1+m3. That's related to the bytecode representation of methods, which includes return types. Therefore, m1+m4 are ok, but not m1+m3.

This is a screwup where Java and JVM specs don't see eye to eye. There is no "correct" way for javac.

While it sucks, the good news is, overloading is a vanity, not a necessity. We can always use different, more descriptive and distinct names for these methods.

like image 98
irreputable Avatar answered Oct 05 '22 08:10

irreputable


Overloading is done at compile-time.

Although the generic parameters are erased at run-time, they're still available to the compiler to resolve overloads.

like image 23
SLaks Avatar answered Oct 05 '22 09:10

SLaks


A Java method signature actually does include the return type; if you've ever worked with JNI you've seen type descriptors like (LPair;)D and (LPair;)I. That last character denotes the return types of your two methods. Although the Java language rule is that the parameters must differ for two overloaded methods, the class file format can actually distinguish methods based only on their return types. When there is generic type information to allow the compiler to resolve the overloads based on their arguments, then as long as the return types are different the erasures will have different signatures, and it all works fine. If the returns types are the same, though, then after erasure the methods have the same signature, the class file can't tell the difference, and you have a problem.

like image 40
Ernest Friedman-Hill Avatar answered Oct 05 '22 09:10

Ernest Friedman-Hill