13

I found a lot of posts about how to overcome this limitation, but none about why this limitation exists (except this one, which just mentions it has to do with type erasure).

So why can't you create an instance of a generic type?

To clarify, my question is not how it can be done. I know it's possible in C#, so why not in Java? I'm curious about why the Java folks did not implement a similar mechanism? Why force Java developers to use awkward workarounds that have the potential to result in a runtime error? Are there any potential hazards from such a mechanism?

Community
  • 1
  • 1
traveh
  • 2,700
  • 3
  • 27
  • 44
  • 2
    Generic types can be anything. So, how would you even call the constructor? – Bubletan Jun 04 '15 at 14:32
  • 1
    In C# you can, by enforcing a 'new' constraint ("specifies that any type argument in a generic class declaration must have a public parameterless constructor"). So technically it is possible which leads me to suggest that it's just not available in the language. – Stephen Kennedy Jun 04 '15 at 14:51
  • 1
    You can most certainly create an instance generic objects – user489041 Jun 04 '15 at 15:00
  • 1
    @user489041 I meant why it's not possible to do it using the new operator. – traveh Jun 04 '15 at 17:16
  • Can you give an example of what you think you should be able to do? I gave my answer based on your [link](http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java) – MadConan Jun 04 '15 at 18:09
  • Sure. Define E as having a default constructor and then do `E e = new E()`. – traveh Jun 04 '15 at 18:12

4 Answers4

17

Short answer: Java is a compiled programming language, which means that your bytecode is constant at runtime. It is impossible to generate bytecode for new E() if E is unknown.

Explanation: Generic information is erased in runtime:

public class Container<E> {
     private E item;
     public E getItem() {return item;}
}
class BoxWithPresent extends Container<Present> {
}
class SimpleBox extends Container {
}

In bytecode class BoxWithPresent contains field item of type Present, but class SimpleBox contains field item of type Object (because type E was not specified).

Now you write abstract instantiation method:

public class Container<E> {
    public <E> E createE() {
        return new E(); // imagine if that was allowed
    }
}

What bytecode should be generated here? .class file is generated right now, at compilation time, but we have no idea what is E type.

So.. can new T() be replaced with new Object()? Bad idea, class BoxWithPresent won't like it, because it expects that E is Present.

Can it be replaced with class.newInstance()? Again no, there is no class variable in method scope.

That's why new E() is impossible.
But there are workarounds with passing class as parameter, or extracting generic information.

Community
  • 1
  • 1
AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • This is the best answer so far, but I did read Oracle documentation about this before asking the question. In fact, that's the reason I asked the question... what I'm curious about is why "it's just not implemented'... – traveh Jun 04 '15 at 17:24
  • BTW, that workaround is also something I sometimes use in my code. It's just ugly and annoying... – traveh Jun 04 '15 at 18:16
  • @traveh I updated the answer, so now you can see the answer on question why – AdamSkywalker Jun 05 '15 at 15:15
  • The short answer is very misleading. C++ is also a compiled language but doesn't have this problem. Rust is another compiled language that doesn't have this problem. Java has this problem because the behaviour of a generic function depends on the dynamic types of its arguments. – Indiana Kernick Mar 16 '21 at 12:00
13

The shortest answer is that generic type parameters do not exist at runtime.

Generics were retrofitted into the Java language in release 5. In order to maintain backward compatibility with the existing code base, they were implemented by erasure.

Generic type parameters exist in your source code at compile-time, but nearly all evidence of them is removed in the byte code during compilation. This implementation of generics was chosen because it maintained inter-operability between pre-generics code and Java 5+ generic code. Type safety with generics is largely, therefore, a compile-time only phenomenon. If your generic code compiles without error and without warnings, then you are assured that your code is type safe.

Because of erasure, however, there are (as of Java 5) two kinds of types:

  • Reifiable. For example String, Integer, etc. A reifiable type has the same type information at compile-time as it has at run-time.

  • Non-reifiable. For example List<String>, List<T>, and T. Non-reifiable types have less type information at run-time that at compile time. In fact, the run-time types of the above are List, List, and Object. During compilation, the generic type information is erased.

You cannot use the new operator with non-reifiable types because there is no type safe way at run-time for the JVM to generate an object of the correct type.

Source code:

T myObject = new T();

The above does not compile. At run-time, T has been erased.

A strategy for circumventing some problems with type erasure and Java generics is to use type tokens. This strategy is implemented in the following generic method that creates a new T object:

public <T> T newInstance(Class<T> cls) {
    T myObject = cls.newInstance();
    return myObject;
}

The generic method captures the type information from the Class object that is passed as a parameter. This parameter is called a type token. Unfortunately, type tokens themselves must always be reifiable (because you can't get a Class object for a non-reifiable type) which can limit their usefulness.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • 1
    As per my understanding the type parameter is replaced with `Object` in byte code if there is no bound. If so, then shouldn't `T t = new T();` be allowed as it will be Object t = new Object(); in the bytecode? – Yug Singh Oct 30 '18 at 19:56
  • 1
    Well, sure ... but that's the problem, isn't it? At run time, you are asking the system to create a new instance of type T, but the JVM doesn't know what T is, and Object can be any instance. Because an Object isn't a T for all Objects, creating any Object and then assigning it to a T would be unsafe and would, then, violate the fundamental guarantee made by the generic type system. – scottb Oct 31 '18 at 05:24
  • 1
    During compile time itself the code `T t = new T();` will become `Object o = new Object();`. So the run-time doesn't about `T`. All it will see that it has to create an instance of Object class and assign to a reference of Object class. Also could you please explain the statement `Because an Object isn't a T for all Objects,`. And how does assigning Object becomes unsafe. Won't the unsafety be dealt at compile time already? – Yug Singh Nov 01 '18 at 21:17
  • 1
    *"During compile time itself the code T t = new T(); will become Object o = new Object();"* ... actually, no it doesn't. The fundamental guarantee of Java's generic type system is that if your code compiles without warning or error, you will never get a ClassCastException. The quoted assignment violates this guarantee for the reasons above. It isn't safe. And so, this assignment never actually compiles into anything at all. – scottb Nov 01 '18 at 23:13
  • @scottb I know it's pretty late for this question. "Because an Object isn't a T for all Objects, creating any Object and then assigning it to a T would be unsafe" ... This is what happens in the backend in every case right? `public class Test { T var; public Test(T var) { this.var=var; } System.out.print(var); }` According to my understanding, in the bytecode T will be replaced by Object and then casted to whichever parameter we're passing (Integer, String etc). Why wouldn't it work here for: T t = new T(); ? – hamal Sep 30 '22 at 04:54
  • @sehrpennt: The code you have written above is typesafe. But not all code that can be written in a routine with `T t = new T();` is always typesafe. It is possible, for example, to create what ought to be a `String` instance and assign it to an `Integer` reference. Moreover, it is possible to pass such unsafely created objects to code far away from the instantiation and build up heap pollution or cause exceptions in hard to debug locations. – scottb Oct 04 '22 at 20:43
1

Creating a class from a generic. Do note that this relies on the class being parametrized. This returns the class object of the generic, through which you can perform further reflection on to create an object.

 public static <T> Class<T> getClassFromGeneric(
            Object parentObj,
            int oridnalParamterizedTypeIndex) throws Exception{
          Type[] typeArray = getParameterizedTypeListAsArray(parentObj);
          return (Class<T>)typeArray[oridnalParamterizedTypeIndex];
    }

    public static <T> Type[] getParameterizedTypeListAsArray(Object parentObj){
        try{
            return  ((ParameterizedType) parentObj.getClass()
                    .getGenericSuperclass())
                    .getActualTypeArguments();
        }
        catch(ClassCastException e){
            logger.log(Level.SEVERE, "Most likely, somewhere in your inhetirance chain,"
                    + "there is a class that uses a raw type and not the generic param."
                    + "See: http://stackoverflow.com/questions/23074446/java-lang-classcastexception-java-lang-class-cannot-be-cast-to-java-lang-reflec"
                    + " for more info",e);
            throw e;
        }
    }

Usage:

public class GenericBaseClass<T>{}
public class GenericImpl extends GenericBaseClass<String>{
    public static void main(String[] args){
        new GenericImpl();
    }

    public GenericImpl(){
        Class tClazz = getClassFromGeneric(this,0);
        Constructor constructor = tClazz.getConstructor();
        T newT = constructor.newInstance();
    }
}

Contrary to popular belief, generic information at the class level is not "erased".

user489041
  • 27,916
  • 55
  • 135
  • 204
  • I hold the popular belief you mention about type erasure. Could you provide a link that might disabuse me of this notion and provide me with enlightenment please? – Phil Anderson Jun 04 '15 at 15:24
  • See my little example. Take a guess what type `newT` will be. The class retains the generic param of `String`, so `newT` will be a String. You can run it for yourself. Believe me, I thought it was magic too when I first saw it. As for an offical reference, see this: https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29 – user489041 Jun 04 '15 at 15:31
  • I'm afraid it's sailing over my head at the moment for some reason. I'll have a google when I have a bit more time. Incidentally, I have no doubt your code works, I just need to clearly understand it before I can convince myself that type erasure is a myth. – Phil Anderson Jun 04 '15 at 15:37
  • @PhilAnderson Just one thing to note, that this is at the class level, not the method level. Thats the one distinction I have to make. – user489041 Jun 04 '15 at 15:38
  • Thanks - I looked at that link and my mind has been ever so slightly blown! – Phil Anderson Jun 04 '15 at 15:45
  • I don't understand how this answers my question. I asked *why* it's not possible. Not how it can be done... – traveh Jun 04 '15 at 17:19
  • @traveh Your original question was `So why can't you create an instance of a generic type?` I showed that your question is flawed because you can indeed create an instance of a generic type. I think the question you asked was not clear. It appears you are asking why you can not do something like `T t = new T()`. If that was what you wanted, you should have been more specific. I simple answered the question as original posed `...` – user489041 Jun 04 '15 at 19:22
  • If you would have read my original question carefully you would have noticed that it also included `I found a lot of posts about how to overcome this limitation` which makes it very clear that I know that it's possible to create an instance of a generic in an alternative way (but not in the "standard" way with "new"). It's also not very nice of you to downvote my question because you didn't bother to read it properly and gave a bad answer that doesn't address the actual question especially after I edited to make it clearer. That's just hate-voting and it's ugly, but whatever. – traveh Jun 04 '15 at 19:52
  • @traveh All is fair in love and stackoverflow. Welome :) – user489041 Jun 04 '15 at 20:20
0

Remember that generic types are about compile-time safety. Compile-time checking of types allows the compiler to give you warnings/errors about issues with your code. This doesn't directly help your question, but it's important to keep the concepts of compile-time and runtime very clear.

You can't say, "return new T()" because the compiler has no way of knowing how to do that. Without the specific type, the compiler can't know what constructor to call or even if it exists. Also, in order to "new up" an instance of type T you need something to call. T is just a symbol. It isn't a Class or an Object. Until you give information about what kind of instance T is (eg List<String>) the compiler has no way of doing what you are trying to do.

Typing is just a way of ensuring that the types given and returned will match correctly at compile-time. It's when you specify type specifics that you are able to create an instance of it. That's why you usually see completely open types on interfaces (eg List<T>, Function<T,R> etc). So implementers of the interface can specify what types will used.

Maybe it helps to think about generics like templates. Not the software development pattern, but the more abstract concept. The concept of a template says there's this structure without any internal details. If you want to create this thing that follows the template, use the template as your starting point, but you must fill in the details to make the thing. A generic type is sort of like that -- it allows you to structure something but doesn't give any details about what happens in that structure.

I know I struggled quite a bit with this when generics were introduced. At first I found it easiest to write the implementation for specific types first and then abstract it out using generics afterwards. After I got my head around generics, I now find it easier to start with a generified interface and then implement it.

MadConan
  • 3,749
  • 1
  • 16
  • 27
  • Generics as a concept are not only about compile time safety; for example in C# you **can** instantiate the generic type. This limitation is caused by the way generics are implemented in Java. – Mark Rotteveel Jun 04 '15 at 15:09
  • Thanks for the effort mate, but I disagree. The compiler does know at compile time which constructor the generic type has, so what's the problem to allow the developer to mark that a class or interface has to have a default constructor, or even a constructor taking specific types of arguments? – traveh Jun 04 '15 at 17:40
  • @traveh Not all types have default constructors. If I define a non-default constructor, it no longer has one. Also, taking specific types arguments is completely different than returning type `T` that is not defined. I thought that was the question. – MadConan Jun 04 '15 at 17:51