在java中实例化genericstypes
重复:Javagenerics为什么这不起作用
重复:在Java中实例化一个generics类
我想在java中创build一个genericstypes的对象。 请build议我怎样才能达到相同的效果。
注意:这似乎是一个微不足道的generics问题。 但我敢打赌,这不是。 🙂
假设我有这样的类声明:
public class Abc<T> { public T getInstanceOfT() { // I want to create an instance of T and return the same. } }
public class Abc<T> { public T getInstanceOfT(Class<T> aClass) { return aClass.newInstance(); } }
你将不得不添加exception处理。
您必须在运行时传递实际的types,因为它不是编译后的字节代码的一部分,所以没有明确提供它就无法知道它。
在你发布的代码中,不可能创build一个T
的实例,因为你不知道是什么types:
public class Abc<T> { public T getInstanceOfT() { // There is no way to create an instance of T here // since we don't know its type } }
当然,如果你有一个对Class<T>
的引用,并且T
有一个默认的构造函数,可以在Class
对象上调用newInstance()
。
如果你Abc<T>
你甚至可以解决types擦除问题,而不必传递任何Class<T>
引用:
import java.lang.reflect.ParameterizedType; public class Abc<T> { T getInstanceOfT() { ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass(); Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0]; try { return type.newInstance(); } catch (Exception e) { // Oops, no default constructor throw new RuntimeException(e); } } public static void main(String[] args) { String instance = new SubClass().getInstanceOfT(); System.out.println(instance.getClass()); } } class SubClass extends Abc<String> { }
你写的东西没有任何意义,Java中的generics意味着将参数多态的function添加到对象中。
这是什么意思? 这意味着你想保留你的类的一些typesvariables未定,以便能够使用你的类与许多不同types。
但是你的typesvariables T
是一个在运行时被parsing的属性,Java编译器会编译你的类的certificatetypes安全性,而不需要知道T
是什么types的对象,所以它不可能让你使用一个typesvariables静态方法。 该types与对象的运行时实例相关联,而public void static main(..)
与类定义相关联,并且在该范围内T
不代表任何内容。
如果要在静态方法中使用typesvariables ,则必须将该方法声明为generics(因为模板类的typesvariables与其运行时实例相关 ),而不是类:
class SandBox { public static <T> void myMethod() { T foobar; } }
这有效,但当然不是main
方法,因为没有办法以通用的方式调用它。
编辑 :问题是,因为types擦除只有一个generics类被编译并传递给JVM。 types检查器只是检查代码是否安全,然后由于它certificate了每种通用信息都被丢弃了。
为了实例化T
你需要知道T
的types,但是它可以同时是多个types,所以一个解决scheme只需要最less的reflection就是使用Class<T>
来实例化新的对象:
public class SandBox<T> { Class<T> reference; SandBox(Class<T> classRef) { reference = classRef; } public T getNewInstance() { try { return reference.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { SandBox<String> t = new SandBox<String>(String.class); System.out.println(t.getNewInstance().getClass().getName()); } }
当然这意味着你想要实例化的types:
- 不是一个原始types
- 它有一个默认的构造函数
要用不同types的构造函数进行操作,必须深入思考。
您需要静态获取types信息。 尝试这个:
public class Abc<T> { private Class<T> clazz; public Abc(Class<T> clazz) { this.clazz = clazz; } public T getInstanceOfT() throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } }
像这样使用它:
Abc<String> abc = new Abc<String>(String.class); abc.getInstanceOfT();
根据你的需要,你可能想要使用Class<? extends T>
Class<? extends T>
而不是。
要使它工作的唯一方法是使用Reified Generics 。 这在Java中不被支持(但是它是为Java 7 计划的,但是已经被推迟了)。 例如在C#中,假设T
有一个默认的构造函数。 您甚至可以通过typeof(T)
获取运行时types,并通过Type.GetConstructor()
获取构造函数。 我不做C#所以语法可能是无效的,但它大致是这样的:
public class Foo<T> where T:new() { public void foo() { T t = new T(); } }
Java中最好的“解决方法”是传递一个Class<T>
作为方法的参数,而不是几个已经指出的答案。
首先,您不能在静态main方法中访问types参数T
,只能在非静态类成员(在这种情况下)中访问。
其次,你不能实例化T
因为Java使用Type Erasure实现了generics。 几乎所有的通用信息都在编译时被删除。
基本上,你不能这样做:
T member = new T();
这是一个很好的generics教程。
你似乎不了解generics如何工作。 你可能想看看http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html基本上你;可以做的是类似的东西
public class Abc<T> { T someGenericThing; public Abc(){} public T getSomeGenericThing() { return someGenericThing; } public static void main(String[] args) { // create an instance of "Abc of String" Abc<String> stringAbc = new Abc<String>(); String test = stringAbc.getSomeGenericThing(); } }
我正在使用以下方法实施相同的方法。
public class Abc<T> { T myvar; public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } }
我试图find一个更好的方式来实现相同的。
这不可能吗?
types擦除解决方法
受@ martin的回答启发,我写了一个帮助类,允许我解决types擦除问题。 使用这个类(和一个小丑的小窍门),我可以创build一个模板types的新实例:
public abstract class C_TestClass<T > { T createTemplateInstance() { return C_GenericsHelper.createTemplateInstance( this, 0 ); } public static void main( String[] args ) { ArrayList<String > list = new C_TestClass<ArrayList<String > >(){}.createTemplateInstance(); } }
这里的丑陋窍门是让类abstract
这样类的用户就被强制子types化了。 这里我通过在构造函数调用之后附加{}
来inheritance它。 这定义了一个新的匿名类并创build它的一个实例。
一旦generics类与具体模板types分类,我能够检索模板types。
public class C_GenericsHelper { /** * @param object instance of a class that is a subclass of a generic class * @param index index of the generic type that should be instantiated * @return new instance of T (created by calling the default constructor) * @throws RuntimeException if T has no accessible default constructor */ @SuppressWarnings( "unchecked" ) public static <T> T createTemplateInstance( Object object, int index ) { ParameterizedType superClass = (ParameterizedType )object.getClass().getGenericSuperclass(); Type type = superClass.getActualTypeArguments()[ index ]; Class<T > instanceType; if( type instanceof ParameterizedType ) { instanceType = (Class<T > )( (ParameterizedType )type ).getRawType(); } else { instanceType = (Class<T > )type; } try { return instanceType.newInstance(); } catch( Exception e ) { throw new RuntimeException( e ); } } }
它看起来像你正在试图创build一个类作为通用的应用程序的入口点,这将无法正常工作… JVM不知道什么types它应该使用时,它被实例化为你启动应用程序。
但是,如果这是更一般的情况下,那么类似的东西就是你要找的东西:
public MyGeneric<MyChoiceOfType> getMeAGenericObject(){ return new MyGeneric<MyChoiceOfType>(); }
也许:
MyGeneric<String> objMyObject = new MyGeneric<String>();
当你真的必须这样做的时候,有一些很奇怪的办法。
下面是一个我发现非常有用的转换方法的例子。 并提供了一种确定generics的具体类的方法。
此方法接受一组对象作为input,并返回一个数组,其中每个元素是对input集合中每个对象调用字段getter的结果。 例如,假设你有一个List<People>
并且你想要一个包含每个人姓氏的String[]
。
由getter返回的字段值的types由genericsE
指定,我需要实例化一个E[]
types的数组来存储返回值。
这个方法本身有点难看,但是你使用它的代码可以更清晰。
请注意,这种技术只有在input参数的某个地方存在一个types与返回types相匹配的对象时才有效,并且可以确定性地将其计算出来。 如果您的input参数(或其子对象)的具体类可以告诉你什么关于generics,那么这种技术将无法正常工作。
public <E> E[] array (Collection c) { if (c == null) return null; if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY; final List<E> collect = (List<E>) CollectionUtils.collect(c, this); final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field); return collect.toArray((E[]) Array.newInstance(elementType, collect.size())); }
完整的代码在这里: https : //github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28
Abc<String> abcInstance = new Abc<String> ();
..例如