2

Let me preface this question by saying up front that I understand what Java can and can't do and am not asking about that. I'm wondering what the actual technical challenges are, from JVM and compiler standpoint, that require the compiler to behave the way it does.

Whenever I see discussions on weaknesses or most hated aspects of java Type Erasure always seems to be somewhere near the top of the list for Java Developers (it is for me!). If my history is correct Java 1.0 never implementing any type checking beyond passing Objects and recasting them. When a better Type system was required Sun had to decide between full Typing support which would break backwards comparability or going with their chosen solution of generics which didn't break old code.

Meanwhile C# ran into the same issue and went the opposite route of breaking backwards comparability to implement a more complex typing system around the same time (I believe).

My main question is why was this a either-or question for the two languages? What is it about the compiler process that means there is no way to support C# style handling of type without breaking backwards comparability in old code? I understand part of the problem is that the exact type is not always known at compile time, but at first (naive) glance it seems like some times it can be known at compile time, or that it can be left unknown at compile time and handled with a sort of reflection approach at runtime.

Is the problem that it's not feasible to implement, or that it was simply deemed too slow to implement a runtime sort of solution?

To go a step further lets use a simple generic factory example of code as an example of a place where type erasure feels rather cumbersome.

public class GenericFactory<FinalType, BuilderType<FinalType> extends GenericBuilder<FinalType>>{

   private Class builderClass;

   public GenericFactory(Class<BuilderType> builderClass){
        this.builderClass=builderClass;
   }

   public FinalType create(){

       GenericBuilder builder=builderClass.newInstance();

       builder.setFoo(getSystemProperty("foo");
       builder.setBar(getSystemProperty("bar");
       builder.setBaz(getSystemProperty("baz");

       return builder.build();
   }
}

This example, assuming I didn't screw up on syntax somewhere, shows two particular annoyances of type erasure that at first glance seem like they should be easier to handle.

First, and less relevant, I had to add a FinalType parameter before I could refer to BuilderType extends GenericBuilder, even though it seems like FinalType could be inferred from BuilderType. I say less relevant since this may be more about generics syntax/implementation then the compiler limits that forced type erasure.

The second issue is that I had to pass in my BuilderClass object to the constructor in order to use reflection to build the builder, despite it being defined by the generics already. It seems as if it would be relatively easy for the compiler to store the generic class used here (so long as it didn't use the ? syntax) to allow reflection to look up the generic and then construct it.

Since this isn't done I presume there is a very good reason it is not. I'm trying to understand what these reasons are, what forces the JVM to stick with type erasure to maintain backwards compatibility?

dsollen
  • 6,046
  • 6
  • 43
  • 84
  • Java 1.0 had no type checking? Please. Possibly you meant something else but as it stands it is ridiculous. – user207421 Apr 05 '16 at 16:14
  • 2
    Possible duplicate of ["What are the differences between Generics in C# and Java… and Templates in C++?"](http://stackoverflow.com/questions/31693), particularly [this answer](http://stackoverflow.com/questions/31693/what-are-the-differences-between-generics-in-c-sharp-and-java-and-templates-i/33729#33729). – Andy Thomas Apr 05 '16 at 16:17

1 Answers1

0

I'm not sure what you're describing (the two "annoyances") are a result of type erasure.

I had to add a FinalType parameter before I could refer to BuilderType extends GenericBuilder, even though it seems like FinalType could be inferred from BuilderType

BuilderType<FinalType> would not be a valid generic type name unless I missed some changes to that in Java 8. Thus it should be BuilderType extends GenericBuilder<FinalType> which is fine. FinalType can't be inferred here, how should the compiler know which type to provide?

The second issue is that I had to pass in my BuilderClass object to the constructor in order to use reflection to build the builder, despite it being defined by the generics already.

That's not true. The generic parameters don't define what FinalType actually is. I could create a GenericFactory<String, StringBuilderType> (with StringBuilderType extends GenericBuilder<String>) as well as a GenericFactory<Integer, IntegerBuilderType> (with IntegerBuilderType extends GenericBuilder<Integer>).

Here, if you'd provide the type parameters to a variable definition or method call, type erasure would happen. As for the why refer to Andy's comment.

However, if you'd have a field or subclass, e.g. private GenericFactory<String, StringBuilderType> stringFactory, there is no type erasure. The generic types can be extracted from the reflection data (unfortunately there's no easy built-in way, but have a look here: http://www.artima.com/weblogs/viewpost.jsp?thread=208860).

Thomas
  • 87,414
  • 12
  • 119
  • 157