Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is return type part of the erasure?

Tags:

java

generics

Can somebody explain why the second class does not compile?

1 Compiles fine using javac and JDK 6 (Eclipse will complain this code)

public class SameSignatureMethods {
    public <T extends String> Boolean test()
    {
        return true;
    }

    public <T extends Character> Double test() 
    {
        return 1d;
    }
}

2 A little change to that example, and compilation fails with the following error:

name clash: <T>test() and <T>test() have the same erasure

The only change is return type on the method:

public class SameSignatureMethods {
    public <T extends String> Boolean test()
    {
        return true;
    }

    public <T extends Character> Boolean test() {
        return true;
    }
}

thats how main method for first class will look:

public static void main(String[] args) {
    SameSignatureMethods m = new SameSignatureMethods();
    System.out.println("m.<Character>test()=" + m.<Character>test());
    System.out.println("m.<String>test()=" + m.<String>test());
}
like image 846
Yuriy Avatar asked Nov 22 '10 19:11

Yuriy


People also ask

What are the two 2 types of Erasure?

- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure.

How does type erasure work?

Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.

What is erasure of method in Java?

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.

What is type erasure and when would you use it?

Type-erasure simply means "erasing" a specific type to a more abstract type in order to do something with the abstract type (like having an array of that abstract type). And this happens in Swift all the time, pretty much whenever you see the word "Any."


2 Answers

So, the JDK compiler compiles the first version but not the second, while the Eclipse compiler compiles neither of the two versions.

From the viewpoint of Java byte code, the first version contains two different methods (after type erasure), namely public java.lang.Boolean test() and public java.lang.Double test(), which is perfectly valid. The JDK compiler and the Eclipse compiler sometimes generate such methods when you override generic methods, but those are then marked as synthetic bridge methods.

The second version would contain two methods with the same signature (after type erasure), which is not allowed in Java byte code. Therefore the JDK compiler cannot generate such a class file. I just edited a class file with a hex editor to create a class with such methods, and upon starting the program, I get this error:

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file SameSignatureMethods
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$000(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: SameSignatureMethods.  Program will exit.

The class I started with looks like this. I used String and Double because they have the same name length:

public class SameSignatureMethods {
    public  <T extends String> String test() {
        return null;
    }

    public  <T extends Double> Double test() {
        return null;
    }

    public static void main(String[] args) {
        System.out.println(new SameSignatureMethods().<Double>test());
    }
}

Then, using a hex editor, I changed the signature of the first method to public <T extends String> Double test(), in two places of the class file, one with the raw signature ()Ljava/lang/Double;, one with the generic signature <T:Ljava/lang/String;>()Ljava/lang/Double;.

like image 105
Christian Semrau Avatar answered Nov 15 '22 04:11

Christian Semrau


Sounds like you have managed to confuse your compiler terribly:

  • Return type is not part of the signature. The compiler can't use return type to tell which method is being called.

  • In your example the generic stuff crammed into the method signature does not affect the return type anyway.

  • Also saying <T extends String> makes no sense, you can't extend a final type. (Hmm, this is just a warning, it does not stop compilation)

You wonder why the second class doesn't compile, I wonder why the first class compiles. As is, the first class compiles, albeit with a warning. Taking out the pointy bracket stuff results in a 'Duplicate Method' error that ought to appear regardless. Must be a compiler bug.

like image 21
Nathan Hughes Avatar answered Nov 15 '22 03:11

Nathan Hughes