Skip to main content

Are you sure you know everything you need about erasure?

Posted by fabriziogiudici on February 5, 2010 at 10:31 AM PST

Erasure is a part of the way Java 5 implements generics, so the bytecode loses all the information about the generified types, only generating the proper type casts where appropriated. It's the thing that you scream against when you try to write code such as:

public abstract class CapabilitiesProviderSupport<T> implements CapabilitiesProvider<T>

  {

    @Override @Nonnull

    public final Class<T> getManagedClass()

      {

        return T.class;

      }



     ...

  }

T.class is not valid, since because of erasure generics are not reified, that is they are not proper objects for which a class literal is available. The typical idiom to solve the problem is to give up to DRY principles and repeat the same information once for the generic (= the compiler) and once for the runtime:

public abstract class CapabilitiesProviderSupport<T> implements CapabilitiesProvider<T>

  {

    @Nonnull

    private final Class<T> managedClass;



    protected CapabilitiesProviderSupport (final Class<T> managedClass)

      {

         this.managedClass = managedClass;

      }

   

    @Override @Nonnull

    public final Class<T> getManagedClass()

      {

        return managedClass;

      }



    ...

  }



public class FooCapabilitiesProvider extends CapabilitiesProviderSupport<Foo>

  {

    public FooCapabilitiesProvider()

      {

        super(Foo.class);

      }



    ...

  }

Nobody will die for this, but it's a minor annoyance.

All right? Not quite. People (me included) always have the bad habit to understand things in a black-white fashion, but sometimes there are shades of gray. For instance, it's true that erasure is present in Java 5 and Java 6, but not in all places. In other words, there are a few cases in which the generic type instantiation information is present at runtime and can retrieved by reflection for useful purposes. For instance, erasure doesn't apply to subclasses of generified classes. So,
it's perfecly possible to write:

public abstract class CapabilitiesProviderSupport<T> implements CapabilitiesProvider<T>

  {

    @Override @Nonnull

    public final Class<T> getManagedClass()

      {

        return (Class<T>)ReflectionUtils.getTypeArgument(CapabilitiesProviderSupport.class, getClass());

      }



     ...

  }



public class FooCapabilitiesProvider extends CapabilitiesProviderSupport<Foo>

  {

     ...

  }

returning to a sane DRY approach. The source for ReflectionUtils is described by Ian Robertson at Artima and a more general discussion, with another example of use, is described by a Neal Gafter blog post.

Related Topics >>

Comments

jdk1.6.0_18 and generics

I noticed that new jdk produces classfiles that are missing a part from bytecode (namely from InnerClasses) that previously was there causing problems with generics.

java.lang.IncompatibleClassChangeError: BarCapabilitiesProvider and BarCapabilitiesProvider$1BarCapabilitiesProviderSupport disagree on InnerClasses attribute
at java.lang.Class.getDeclaringClass(Native Method)
...
at java.lang.Class.getGenericSuperclass(Class.java:677)

It seems that BarCapabilitiesProviderSupport is missing from BarCapabilitiesProvider bytecode.

public class BarCapabilitiesProvider extends CapabilitiesProviderSupport {

public static CapabilitiesProvider newInstance() {
abstract class BarCapabilitiesProviderSupport extends BarCapabilitiesProvider {}
final class BarCapabilitiesProviderImpl extends BarCapabilitiesProviderSupport {}
return new BarCapabilitiesProviderImpl();
}

}

Testing is relatively with following lines.
CapabilitiesProvider capabilitiesProvider = BarCapabilitiesProvider.newInstance();
capabilitiesProvider.getManagedClass();

public class

public class BarCapabilitiesProvider extends CapabilitiesProviderSupport { public static CapabilitiesProvider newInstance() { abstract class BarCapabilitiesProviderSupport extends BarCapabilitiesProvider { } final class BarCapabilitiesProvider extends BarCapabilitiesProviderSupport { } return new BarCapabilitiesProvider (); } }

generics is "cheating"

The meta information of generics type can't be gotten in runtime,just ensure the type safe and cheat the user in compile-time.

Bug in second example

Shouldn't the getManagedClass() method in your second example return managedClass and not duplicate the bug in the first? This technique is one I use fairly often when I have to know the type of a generic. It is a pain but one that I can live with as generics are very useful - they can just be a bit messy at times - can't wait for the new <> syntax from project Coin...

Sure, Peter, fixed - thanks.

Sure, Peter, fixed - thanks.

tons of empty classes

The boring detail of generics is to force the Java developers to create tons of empty classes, just to avoid the ambiguous reference in Runtime.. so a typical Java EE applications contains a package with 1 empty class for every Entity.. :(