Create instance of generic type in Java?

后端 未结 27 3074
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-21 06:14

Is it possible to create an instance of a generic type in Java? I\'m thinking based on what I\'ve seen that the answer is no (due to type erasure), but

相关标签:
27条回答
  • 2020-11-21 06:39

    I thought I could do that, but quite disappointed: it doesn't work, but I think it still worths sharing.

    Maybe someone can correct:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    interface SomeContainer<E> {
        E createContents();
    }
    
    public class Main {
    
        @SuppressWarnings("unchecked")
        public static <E> SomeContainer<E> createSomeContainer() {
            return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                    new Class[]{ SomeContainer.class }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Class<?> returnType = method.getReturnType();
                    return returnType.newInstance();
                }
            });
        }
    
        public static void main(String[] args) {
            SomeContainer<String> container = createSomeContainer();
    
        [*] System.out.println("String created: [" +container.createContents()+"]");
    
        }
    }
    

    It produces:

    Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
        at Main.main(Main.java:26)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
    

    Line 26 is the one with the [*].

    The only viable solution is the one by @JustinRudd

    0 讨论(0)
  • 2020-11-21 06:41

    Use the TypeToken<T> class:

    public class MyClass<T> {
        public T doSomething() {
            return (T) new TypeToken<T>(){}.getRawType().newInstance();
        }
    }
    
    0 讨论(0)
  • 2020-11-21 06:42

    Hope this's not too late to help!!!

    Java is type-safe, meaning that only Objects are able to create instances.

    In my case I cannot pass parameters to the createContents method. My solution is using extends unlike the answer below.

    private static class SomeContainer<E extends Object> {
        E e;
        E createContents() throws Exception{
            return (E) e.getClass().getDeclaredConstructor().newInstance();
        }
    }
    

    This is my example case in which I can't pass parameters.

    public class SomeContainer<E extends Object> {
        E object;
    
        void resetObject throws Exception{
            object = (E) object.getClass().getDeclaredConstructor().newInstance();
        }
    }
    

    Using reflection create run time error, if you extends your generic class with none object type. To extends your generic type to object convert this error to compile time error.

    0 讨论(0)
  • 2020-11-21 06:42

    As you said, you can't really do it because of type erasure. You can sort of do it using reflection, but it requires a lot of code and lot of error handling.

    0 讨论(0)
  • 2020-11-21 06:43

    If you need a new instance of a type argument inside a generic class then make your constructors demand its class...

    public final class Foo<T> {
    
        private Class<T> typeArgumentClass;
    
        public Foo(Class<T> typeArgumentClass) {
    
            this.typeArgumentClass = typeArgumentClass;
        }
    
        public void doSomethingThatRequiresNewT() throws Exception {
    
            T myNewT = typeArgumentClass.newInstance();
            ...
        }
    }
    

    Usage:

    Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
    Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);
    

    Pros:

    • Much simpler (and less problematic) than Robertson's Super Type Token (STT) approach.
    • Much more efficient than the STT approach (which will eat your cellphone for breakfast).

    Cons:

    • Can't pass Class to a default constructor (which is why Foo is final). If you really do need a default constructor you can always add a setter method but then you must remember to give her a call later.
    • Robertson's objection... More Bars than a black sheep (although specifying the type argument class one more time won't exactly kill you). And contrary to Robertson's claims this does not violate the DRY principal anyway because the compiler will ensure type correctness.
    • Not entirely Foo<L>proof. For starters... newInstance() will throw a wobbler if the type argument class does not have a default constructor. This does apply to all known solutions though anyway.
    • Lacks the total encapsulation of the STT approach. Not a big deal though (considering the outrageous performance overhead of STT).
    0 讨论(0)
  • 2020-11-21 06:44

    Think about a more functional approach: instead of creating some E out of nothing (which is clearly a code smell), pass a function that knows how to create one, i.e.

    E createContents(Callable<E> makeone) {
         return makeone.call(); // most simple case clearly not that useful
    }
    
    0 讨论(0)
提交回复
热议问题