Create instance of generic type in Java?
是否可以在Java中创建泛型类型的实例?我的想法是基于我所看到的答案是
1 2 3 4 5 6 7 | class SomeContainer<E> { E createContents() { return what??? } } |
编辑:事实证明,超级类型的令牌可以用来解决我的问题,但是它需要很多基于反射的代码,正如下面的一些答案所指出的那样。
我会把这个打开一段时间,看看是否有人想出任何明显不同于伊恩罗伯逊的Artima文章。
你是对的。你不能做
1 2 3 4 5 | private static class SomeContainer<E> { E createContents(Class<E> clazz) { return clazz.newInstance(); } } |
这是一种痛苦。但它有效。用工厂的方式包装它会让它更容易接受。
不知道,如果这有帮助,但是当您对一个泛型类型进行子类(包括匿名)时,类型信息可以通过反射获得。例如。,
1 2 3 4 5 6 7 8 9 10 11 | public abstract class Foo<E> { public E instance; public Foo() throws Exception { instance = ((Class)((ParameterizedType)this.getClass(). getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); ... } } |
因此,当您将foo子类化时,会得到一个bar的实例,例如,
1 2 | // notice that this in anonymous subclass of Foo assert( new Foo<Bar>() {}.instance instanceof Bar ); |
但是这是一个很大的工作,而且只适用于子类。不过也很方便。
在Java 8中,您可以使用EDCOX1 6功能接口来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 | class SomeContainer<E> { private Supplier<E> supplier; SomeContainer(Supplier<E> supplier) { this.supplier = supplier; } E createContents() { return supplier.get(); } } |
您可以这样构造这个类:
1 |
该行的语法
如果构造函数接受参数,则可以改用lambda表达式:
1 2 |
你需要某种抽象的工厂来把责任转嫁给:
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface Factory<E> { E create(); } class SomeContainer<E> { private final Factory<E> factory; SomeContainer(Factory<E> factory) { this.factory = factory; } E createContents() { return factory.create(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package org.foo.com; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * Basically the same answer as noah's. */ public class Home<E> { @SuppressWarnings ("unchecked") public Class<E> getTypeParameterClass() { Type type = getClass().getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) type; return (Class<E>) paramType.getActualTypeArguments()[0]; } private static class StringHome extends Home<String> { } private static class StringBuilderHome extends Home<StringBuilder> { } private static class StringBufferHome extends Home<StringBuffer> { } /** * This prints"String","StringBuilder" and"StringBuffer" */ public static void main(String[] args) throws InstantiationException, IllegalAccessException { Object object0 = new StringHome().getTypeParameterClass().newInstance(); Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance(); Object object2 = new StringBufferHome().getTypeParameterClass().newInstance(); System.out.println(object0.getClass().getSimpleName()); System.out.println(object1.getClass().getSimpleName()); System.out.println(object2.getClass().getSimpleName()); } } |
如果需要泛型类内类型参数的新实例,则使构造函数要求其类…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); ... } } |
用途:
1 2 | Foo<Bar> barFoo = new Foo<Bar>(Bar.class); Foo<Etc> etcFoo = new Foo<Etc>(Etc.class); |
赞成的意见:
- 比罗伯逊的超类型令牌(STT)方法简单得多(问题也更少)。
- 比STT方法更有效(它会在早餐时吃掉你的手机)。
欺骗:
- 无法将类传递给默认构造函数(这就是foo是final的原因)。如果您确实需要一个默认的构造函数,您可以始终添加一个setter方法,但之后必须记住给她一个调用。
- 罗伯逊反对…更多的条比一个黑绵羊(尽管再次指定类型参数类不会完全杀死您)。与罗伯逊的说法相反,这无论如何都不会违反dry原则,因为编译器将确保类型的正确性。
- 不完全是
Foo 证据。首先……如果类型参数类没有默认的构造函数,newInstance() 将抛出一个摇摆器。不过,这确实适用于所有已知的解决方案。 - 缺乏STT方法的完全封装。不过没什么大不了的(考虑到STT的惊人的性能开销)。
您现在可以这样做,它不需要一堆反射代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import com.google.common.reflect.TypeToken; public class Q26289147 { public static void main(final String[] args) throws IllegalAccessException, InstantiationException { final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {}; final String string = (String) smpc.type.getRawType().newInstance(); System.out.format("string = "%s"",string); } static abstract class StrawManParameterizedClass<T> { final TypeToken<T> type = new TypeToken<T>(getClass()) {}; } } |
当然,如果您需要调用需要一些反射的构造函数,但这是非常好的文档记录,那么这个技巧就不是了!
这是用于typetoken的javadoc。
考虑一个更实用的方法:不要凭空创建一些e(这显然是一种代码味道),而是传递一个知道如何创建e的函数,即。
1 2 3 | E createContents(Callable<E> makeone) { return makeone.call(); // most simple case clearly not that useful } |
从Java教程-对泛型的限制:
无法创建类型参数的实例
无法创建类型参数的实例。例如,以下代码会导致编译时错误:
1 2 3 4 | public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); } |
作为解决方法,您可以通过反射创建类型参数的对象:
1 2 3 4 | public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } |
您可以调用append方法,如下所示:
1 2 |
如果不想在实例化期间键入两次类名,如:
1 | new SomeContainer<SomeType>(SomeType.class); |
您可以使用工厂方法:
1 | <E> SomeContainer<E> createContainer(Class<E> class); |
喜欢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Container<E> { public static <E> Container<E> create(Class<E> c) { return new Container<E>(c); } Class<E> c; public Container(Class<E> c) { super(); this.c = c; } public E createInstance() throws InstantiationException, IllegalAccessException { return c.newInstance(); } } |
以下是我提出的一个选项,它可能有助于:
1 2 3 4 5 6 7 8 9 10 11 |
编辑:也可以使用此构造函数(但它需要e的实例):
1 2 3 4 | @SuppressWarnings("unchecked") public Container(E instance) { this.clazz = (Class<E>) instance.getClass(); } |
JAVA不幸的是不允许你想做什么。参见官方解决方案:
You cannot create an instance of a type parameter. For example, the following code causes a compile-time error:
1 2 3 4 | public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); } |
As a workaround, you can create an object of a type parameter through reflection:
1 2 3 4 | public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } |
You can invoke the append method as follows:
1 2 |
在编译时使用e时,实际上并不关心实际的泛型类型"e"(使用反射或使用泛型类型的基类),因此让子类提供e的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Abstract class SomeContainer<E> { abstract protected E createContents(); public doWork(){ E obj = createContents(); // Do the work with E } } **BlackContainer extends** SomeContainer<Black>{ Black createContents() { return new Black(); } } |
你可以使用:
1 |
但是您需要提供准确的类名,包括包,例如
我以为我能做到,但很失望:它不起作用,但我认为它仍然值得分享。
也许有人可以纠正:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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()+"]"); } } |
它产生:
1 2 3 4 5 6 7 | 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) |
第26行是带有
唯一可行的解决方案是@justinrudd提供的解决方案
对诺亚回答的否定。
变更原因
如果在更改顺序的情况下使用多个泛型类型,则A]更安全。
B]类的泛型类型签名会不时更改,这样您就不会对运行时中无法解释的异常感到惊讶。
鲁棒代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public abstract class Clazz<P extends Params, M extends Model> { protected M model; protected void createModel() { Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments(); for (Type type : typeArguments) { if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) { try { model = ((Class<M>) type).newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } } |
或者用一个衬垫
单行代码
1 | model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance(); |
您可以通过以下代码片段来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.lang.reflect.ParameterizedType; public class SomeContainer<E> { E createContents() throws InstantiationException, IllegalAccessException { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); @SuppressWarnings("unchecked") Class<E> clazz = (Class<E>) genericSuperclass.getActualTypeArguments()[0]; return clazz.newInstance(); } public static void main( String[] args ) throws Throwable { SomeContainer< Long > scl = new SomeContainer<>(); Long l = scl.createContents(); System.out.println( l ); } } |
1 | return (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); |
正如你所说,你不能真的去做,因为类型删除。您可以使用反射来完成它,但是它需要大量的代码和大量的错误处理。
有各种各样的库可以使用类似于Robertson文章讨论的技术为您解析
1 2 3 | E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } |
这假定getClass()解析为SomeContainer的子类,否则将失败,因为如果E的实际参数化值未捕获到子类中,那么它将在运行时被擦除。
如果你是说
1 2 3 4 5 6 7 8 9 10 | interface Factory<E>{ E create(); } class IntegerFactory implements Factory<Integer>{ private static int i = 0; Integer create() { return i++; } } |
下面是
1 2 3 | E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } |
此方法仅在
1 | class SomeStringContainer extends SomeContainer<String> |
否则,e的值在运行时被擦除,不可恢复。
希望这不会太晚帮不上忙!!!!
Java是类型安全的,只有对象是能够创建实例的。
在我的例子中,我不能将参数传递给
1 2 3 4 5 6 | private static class SomeContainer<E extends Object> { E e; E createContents() throws Exception{ return (E) e.getClass().getDeclaredConstructor().newInstance(); } } |
这是我的例子,我不能传递参数。
1 2 3 4 5 6 7 | public class SomeContainer<E extends Object> { E object; void resetObject throws Exception{ object = (E) object.getClass().getDeclaredConstructor().newInstance(); } } |
如果用无对象类型扩展泛型类,则使用反射创建运行时错误。若要将泛型类型扩展到对象,请将此错误转换为编译时错误。
可以使用类加载器和类名,最后是一些参数。
1 2 3 4 5 | final ClassLoader classLoader = ... final Class<?> aClass = classLoader.loadClass("java.lang.Integer"); final Constructor<?> constructor = aClass.getConstructor(int.class); final Object o = constructor.newInstance(123); System.out.println("o =" + o); |
这里有一个改进的解决方案,基于
实施中的第一个小改进。工厂不应返回实例,而应返回类型。一旦您使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class TypeReference<T> { public Class<T> type(){ try { ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){ throw new IllegalStateException("Could not define type"); } if (pt.getActualTypeArguments().length != 1){ throw new IllegalStateException("More than one type has been found"); } Type type = pt.getActualTypeArguments()[0]; String typeAsString = type.getTypeName(); return (Class<T>) Class.forName(typeAsString); } catch (Exception e){ throw new IllegalStateException("Could not identify type", e); } } } |
下面是一个用法示例。@拉尔斯·波尔只展示了一种通过扩展获得身份化的通用性的签名方式。@noah只能通过使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import java.lang.reflect.Constructor; public class TypeReferenceTest { private static final String NAME ="Peter"; private static class Person{ final String name; Person(String name) { this.name = name; } } @Test public void erased() { TypeReference<Person> p = new TypeReference<>(); Assert.assertNotNull(p); try { p.type(); Assert.fail(); } catch (Exception e){ Assert.assertEquals("Could not identify type", e.getMessage()); } } @Test public void reified() throws Exception { TypeReference<Person> p = new TypeReference<Person>(){}; Assert.assertNotNull(p); Assert.assertEquals(Person.class.getName(), p.type().getName()); Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass()); Assert.assertNotNull(ctor); Person person = (Person) ctor.newInstance(NAME); Assert.assertEquals(NAME, person.name); } static class TypeReferencePerson extends TypeReference<Person>{} @Test public void reifiedExtenension() throws Exception { TypeReference<Person> p = new TypeReferencePerson(); Assert.assertNotNull(p); Assert.assertEquals(Person.class.getName(), p.type().getName()); Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass()); Assert.assertNotNull(ctor); Person person = (Person) ctor.newInstance(NAME); Assert.assertEquals(NAME, person.name); } } |
注意:当通过将类抽象为: