Generics / Erasure, or why does it fail?

Note that the post below is a cross-post. Main thread is in the generics forum

Main thread: http://forum.java.sun.com/thread.jspa?threadID=769830

Hi all,

I've been reading the generics tutorial, http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf, and the chapter on generics in The Java Programming Language, Fourth Edition, and still I can't find anything which explains why the commented line, case 1, in this code fails to compile.

Why doesn't it compile? Does it have anything to do with erasure? Why doesn't the compiler know that E is a subclass of Shape? I've been reading a bit about erasure and thought that E would be replaced with Shape during compilation. The output from javap does also indicate this.

The complete java code:

import java.awt.Rectangle;

import java.awt.Shape;

import java.util.ArrayList;

publicclass Aggregator<Eextends Shape>{

private ArrayList<E> elements;

public Aggregator(){

this.elements =new ArrayList<E>();

}

publicvoid add(E e){

elements.add(e);

}

public String toString(){

return elements.toString();

}

publicvoid populateWithTestData(){

//add(new Rectangle());// ## case 1

add((E)new Rectangle());// ## case 2

}

publicstaticvoid main(String[] args){

Aggregator<Rectangle> aggregator =new Aggregator<Rectangle>();

aggregator.add(new Rectangle());// ## case 3 this works fine

}

}

Case 1 does not compile and gives:

The method add(E) in the type Aggregator<E> is not applicable for

the arguments (Rectangle)

Case 2 compiles with a warning, and gives:

Type safety: The cast from Rectangle to E is actually checking

against the erased type Shape

Why does case 3 compile if case 1 doesn't?

Output from javap (Removed output for constructor and toString to reduce the post):

javap -c -classpath . -private -s Aggregator

Compiled from "Aggregator.java"

public class Aggregator extends java.lang.Object{

private java.util.ArrayList elements;

Signature: Ljava/util/ArrayList;

public void add(java.awt.Shape);

Signature: (Ljava/awt/Shape;)V

Code:

0:aload_0

1:getfield#17; //Field elements:Ljava/util/ArrayList;

4:aload_1

5:invokevirtual#28; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z

8:pop

9:return

public void populateWithTestData();

Signature: ()V

Code:

0:aload_0

1:new#39; //class java/awt/Rectangle

4:dup

5:invokespecial#41; //Method java/awt/Rectangle."<init>":()V

8:invokevirtual#42; //Method add:(Ljava/awt/Shape;)V

11: return

public static void main(java.lang.String[]);

Signature: ([Ljava/lang/String;)V

Code:

0:new#1; //class Aggregator

3:dup

4:invokespecial#46; //Method "<init>":()V

7:astore_1

8:aload_1

9:new#39; //class java/awt/Rectangle

12: dup

13: invokespecial#41; //Method java/awt/Rectangle."<init>":()V

16: invokevirtual#42; //Method add:(Ljava/awt/Shape;)V

19: return

}

Does anyone know where I should look to find information on why case 1 doesn't compile? Can anyone explain the problem?

Thanks

Kaj

[4524 byte] By [kajbja] at [2007-10-3 5:23:55]
# 1

I see only one difference: on case 3, the type is defined as Rectangle. On case 1, there's no type definition yet: the argument has to be of type <E>, which could be anything - but you supply a Rectangle. Make that E a String, and the argument doesn't match.

With the cast in case 2, you define the argument type to be E (<-- is that the "erasure"?) and thus it works again. No verifyable type safety in case 1.

But who am I to talk about Generics, I have no idea.

CeciNEstPasUnProgrammeura at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...
# 2

> I see only one difference: on case 3, the type is

> defined as Rectangle.

Ok, I declaration to:

Aggregator<Shape> aggregator = new Aggregator<Shape>();

And that gives the same result.

> On case 1, there's no type

> definition yet:

I can understand that, but doesn't this:

<E extends Shape>

Tell the compiler that E is undefined but it has to be something which at least is of type Shape?

> the argument has to be of type <E>,

Why? When it's declared as <E extends Shape>?

> which could be anything - but you supply a Rectangle.

> Make that E a String, and the argument doesn't

> match.

>

> With the cast in case 2, you define the argument type

> to be E (<-- is that the "erasure"?) and thus it

> works again. No verifyable type safety in case 1.

>

> But who am I to talk about Generics, I have no idea.

Your guesses are as good as mine. I haven't used generics that much.

kajbja at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...
# 3

> > the argument has to be of type <E>,

>

> Why? When it's declared as <E extends Shape>?

Isn't that just the requirement that whatever the type for the Generic has to extend Shape? It could be Rectangle, yes. But it still also could be Oval. And again you'd have a type mismatch with Rectangle.

CeciNEstPasUnProgrammeura at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...
# 4

> > > the argument has to be of type <E>,

> >

> > Why? When it's declared as <E extends Shape>?

>

> Isn't that just the requirement that whatever the

> type for the Generic has to extend Shape? It

> could be Rectangle, yes. But it still also could be

> Oval. And again you'd have a type mismatch with

> Rectangle.

Ah, that's true. Got it. Thanks :)

kajbja at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...
# 5
Wow. That almost made me feel intelligent. :)
CeciNEstPasUnProgrammeura at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...
# 6

The idea is that the template should not fail for all the legal implementations. Because you defined E as unknown, you cannot add anything that is not explicitly of type E in the Aggregator class. Remember the implementation will define Aggregator for only one subclass of Shape, so the implementation of the template should only accept objects of one type and that is type E

r035198xa at 2007-7-14 23:31:02 > top of Java-index,Java Essentials,Java Programming...