Type Erasure + Method Overloading interaction?

I wrote a set of classes intended to represent a DatabaseID, and I wanted to make use of generics so that the appropriate overloaded equals() would be called. What I ended up doing was perhaps figuring out a way around type erasure. I've stripped everything down to make it relevant for this post:

// Base class that does most of the work, abstract.

publicabstractclass DatabaseID < ConcreteIDextends DatabaseID >

{

protected

int

id;

protected

DatabaseID (int newID )

{

id = newID;

}

// Overloaded equals(), specific to the parameterized type

public

boolean

equals ( ConcreteID otherID )

{

System.out.println("Overloaded equals() called." );

return id == otherID.id;

}

// Overridden equals() that simply compares Objects

public

boolean

equals ( Object o )

{

System.out.println("Object equals() called." );

returnfalse;

}

}

// Concrete subclass of DatabaseID representing an OrderID

publicclass OrderIDextends DatabaseID< OrderID >

{

// Contains nothing but a constructor to initialize its value

public

OrderID (int newID )

{

super( newID );

}

}

// Concrete subclass of DatabaseID representing a UserID

publicclass UserIDextends DatabaseID< UserID >

{

// Contains nothing but a constructor to initialize its value

public

UserID (int newID )

{

super( newID );

}

}

// The test class

publicclass Test

{

publicstatic

void

main ( String[] argv )

{

OrderID o =new OrderID( 1 );

UserID u1 =new UserID( 1 );

UserID u2 =new UserID( 1 );

u1.equals( u2 );// Calls overloaded equals()

o.equals( u2 );// Calls equals( Object )

}

}

What surprised me was that I had expected type erasure to cause both of these calls to resolve to the overloaded equals(). Since I had defined the overloaded equals to take <ConcreteID extends DatabaseID>, the overloaded equals will ultimately erase to equals( DatabaseID ). Since both ID's are DatabaseID objects, I had expected that that version of equals() is the one that would have been called.

But if you run the code, you'll see that in the first case, where two UserID objects are being compared, the overloaded equals() is being called. But in the second case, where an OrderID object is being compared to a UserID object, the "normal" equals() that compares regular Objects is called.

At first I wondered what was going on, and then I realized that it's the compiler that chooses which overloaded method is called. The compiler is the one that looks at the types of the objects and determines which version of a method should get executed. Because this is done by the compiler, the compiler is still able to see the un-erased types, and thus in the second case it knows that two different types of objects are being compared, and chooses the equals( Object ) version. This can be confirmed by looking at the byte code of the Test class and seeing that in the first case, the compiler has inserted a call to the overloaded equals() method, but in the second case it has inserted a call to the regular equals() method.

So, method overloading is one case where you can use generics and type erasure won't come into play.

[5539 byte] By [PARSIa] at [2007-10-2 18:50:06]
# 1
Erasure is only for the run time. As you say, the binding of methods to calls is done at compile time so generics information is available.
malcolmmca at 2007-7-13 20:12:57 > top of Java-index,Java Essentials,Java Programming...