在java中实例化泛型类型

时间:2010-03-12 16:15:06

标签: java generics

  

Duplicate: Java generics why this won’t work

     

Duplicate: Instantiating a generic class in Java

我想在java中创建一个Generics Type对象。请建议我如何实现同样的目标。

注意:这似乎是一个微不足道的泛型问题。但我敢打赌......事实并非如此。 :)

假设我有类声明:

public class Abc<T> {
    public T getInstanceOfT() {
       // I want to create an instance of T and return the same.
    }
}

12 个答案:

答案 0 :(得分:65)

public class Abc<T> {
    public T getInstanceOfT(Class<T> aClass) {
       return aClass.newInstance();
    }
}

您必须添加异常处理。

你必须在运行时传递实际类型,因为它不是编译后字节代码的一部分,所以没有明确地提供它就无法知道它。

答案 1 :(得分:25)

在您发布的代码中,由于您不知道它是什么类型,因此无法创建T的实例:

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有默认构造函数,则可以在newInstance()对象上调用Class

如果您继承Abc<T>,您甚至可以解决类型擦除问题,并且不必传递任何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>
{
}

答案 2 :(得分:13)

您所写的内容没有任何意义,Java中的泛型旨在将参数多态的功能添加到对象中。

这是什么意思?这意味着您希望保留未定义的类的某些类型变量,以便能够使用具有许多不同类型的类。

但是你的类型变量 T是一个在运行时解析的属性,Java编译器将编译你的类证明类型安全而不试图知道什么类型的对象{ {1}}所以它不可能让你在静态方法中使用类型变量。该类型与对象的运行时实例相关联,而T与类定义相关联,并且在该范围public void static main(..)并不意味着什么。

如果要在静态方法中使用类型变量,则必须将该方法声明为泛型(这是因为,如模板类的类型变量所解释的那样)与其运行时实例相关,而不是类:

T

这样可行,但当然不能使用class SandBox { public static <T> void myMethod() { T foobar; } } 方法,因为无法以通用方式调用它。

编辑:问题在于,由于类型擦除,只编译了一个泛型类并将其传递给JVM。类型检查器只是检查代码是否安全,然后证明它被丢弃了各种通用信息

要实例化main,您需要知道T的类型,但它可以同时包含多种类型,因此只需要最少量反射的一个解决方案就是使用{{ 1}}实例化新对象:

T

当然这意味着您想要实例化的类型:

  • 不是原始类型
  • 它有一个默认构造函数

要与不同类型的构造函数一起操作,您必须深入研究反射。

答案 3 :(得分:5)

您需要静态获取类型信息。试试这个:

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>

答案 4 :(得分:4)

使其发挥作用的唯一方法是使用Reified Generics。这在Java中不受支持(但它已经计划用于Java 7,但已被推迟)。例如,在C#中,假设T具有默认构造函数,则支持它。您甚至可以通过typeof(T)获取运行时类型,并通过Type.GetConstructor()获取构造函数。我不做C#所以语法可能无效,但它大致如下:

public class Foo<T> where T:new() {
    public void foo() {
        T t = new T();
    }
}

Java中最好的“解决方法”是传递Class<T>作为方法参数,而不是已经指出的几个答案。

答案 5 :(得分:3)

首先,您不能在静态main方法中访问类型参数T,只能访问非静态类成员(在本例中)。

其次,您无法实例化T,因为Java使用 Type Erasure 实现泛型。几乎所有通用信息都在编译时被删除。

基本上,你不能这样做:

T member = new T();

以下是关于generics的精彩教程。

答案 6 :(得分:2)

您似乎并不了解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();
  }

} 

答案 7 :(得分:1)

我正在使用以下方法实现相同的目标。

public class Abc<T>
{
   T myvar;

    public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
    {
        return clazz.newInstance();
    }
}

我试图找到一种更好的方法来实现同样的目标。

不可能吗?

答案 8 :(得分:1)

类型擦除解决方法

@martin's answer的启发,我编写了一个帮助程序类,允许我解决类型擦除问题。使用这个类(以及一个有点丑陋的技巧)我可以用模板类型创建一个新实例:

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,因此强制类的用户对其进行子类型化。在这里,我通过在调用构造函数之后附加{}来对其进行子类化。这定义了一个新的匿名类并创建了它的实例。

一旦使用具体的模板类型对泛型类进行了子类型化,我就能够检索模板类型。


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 );
        }
    }
}

答案 9 :(得分:0)

看起来你正在尝试创建一个类作为通用的应用程序的入口点,并且不起作用...... JVM将不知道它应该在什么类型时使用它在你启动应用程序时被实例化。

然而,如果这是更一般的情况,那么你正在寻找的东西:

public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
   return new MyGeneric<MyChoiceOfType>();
}

或者也许:

MyGeneric<String> objMyObject = new MyGeneric<String>();

答案 10 :(得分:0)

当你真的必须这样做时,有很多方法可以解决这个问题。

这是我觉得非常有用的变换方法的一个例子;并提供了一种确定泛型的具体类的方法。

此方法接受一组对象作为输入,并返回一个数组,其中每个元素都是在输入集合中的每个对象上调用字段getter的结果。例如,假设您有一个List<People>,并且您希望String[]包含每个人的姓氏。

getter返回的字段值的类型由泛型E指定,我需要实例化一个类型E[]的数组来存储返回值。

该方法本身有点难看,但您编写的使用它的代码可以更清晰。

请注意,此技术仅在输入参数中的某个位置存在时,其类型与返回类型匹配,并且您可以确定地将其计算出来。如果输入参数(或其子对象)的具体类可以告诉您关于泛型的任何信息,那么这种技术将不起作用。

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

答案 11 :(得分:-3)

Abc<String> abcInstance = new Abc<String> ();

..例如