Why Java Cannot Create Instances of Type Parameters

3k Views Asked by At

I know Java does not allow not Create Instances of Type Parameters. Many articles simply said "Type Erase" as the reason. But does type parameters initialization not occur before type erase? Is Type Erase the only reason? Here is a example:

public class GenObj {
    public static <E> void append(List<E> list) {
        E elem = new E();  // compile-time error
        list.add(elem);
    }
    public static main(){
        List<String> list= new ArrayList<String>();
        GenOjb.append<String>(list);
    }
}

When we call the generic method using GenOjb.append(list), I think the compiler will replace E in the method with String first and then do "Type Erase", is that correct? If so, as long as we have a way to ensure E does indeed have a default constructor, we should be able to create instance of type parameters. Can someone explain in more detail why Java does not allow creating instance of parameter type? Thanks.

4

There are 4 best solutions below

0
On

Remember that Generics are for compile time checking and that when you compile a class within .java file, Java produces a single .class file for that class.

When we call the generic method using GenOjb.append(list); I think compiler will replace E in the method with String first and then do "Type Erase", is that correct?

No, nothing is replaced in the method. Once the .class file is generated, that's what you get. When compiling, the compiler simply verifies that the String type is an acceptable type argument for the append() method. Since you've specified no bounds on E, the compiler judges that String is an acceptable type argument.

Can someone explain in more details why java not allow creating instance of parameter type?

That's just not how the java language works. Class instantiation happens at run time. At run time, there is no longer any notion of type variables because of type erasure and therefore we cannot know what E is.

There are a few alternatives for getting an instance of whatever type T ends up being. See here:

0
On

Because under the covers, a List<E> is just a List. The actual type that has been substituted for E is not passed on the stack. Therefore, when the JVM is running this code, there is no way to tell what type E is, so it has no way of knowing what class to instantiate.

1
On

Due to runtime type erasure, the type is not available to do anything with.

You can however pass a type token:

public static <E> void append(List<E> list, Class<E> c) {
    E elem = c.newInstance();
    list.add(elem);
}

This presupposes that the class has a no-args constructor.

4
On

It is instructive to ask: How would you do it without Generics?

Every program with Generics can be converted into an equivalent program without Generics by simply removing generic parameters and inserting casts in appropriate places. This is called type erasure. So, if you want to know if you can do it with Generics, you need to first ask if you can do it without Generics.

Without Generics, your program looks like this:

public class GenObj {
    public static void append(List list) {
        Object elem = // what goes here?
        list.add(elem);
    }
    public static main(){
        List list= new ArrayList();
        GenOjb.append(list);
    }
}