在java中实例化泛型类型

Moh*_*rid 57 java generics

重复:Java泛型为什么这不起作用

重复:在Java中实例化泛型类

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

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

假设我有类声明:

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

Jen*_*der 69

public class Abc<T> {
    public T getInstanceOfT(Class<T> aClass) {
       return aClass.newInstance();
    }
}
Run Code Online (Sandbox Code Playgroud)

您必须添加异常处理.

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

  • 你是如何使用这种方法的?换句话说,电话会是什么样子?`T newT = this.getInstance(T.class)`将无法编译. (5认同)
  • [`clazz.newInstance()` 自 Java 9 起已弃用](https://docs.oracle.com/javase/9​​/docs/api/java/lang/Class.html#newInstance--):_可以替换通过 `clazz.getDeclaredConstructor().newInstance()`_。 (2认同)

Mar*_*tin 26

在您发布的代码中,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
       }
} 
Run Code Online (Sandbox Code Playgroud)

当然,如果您有一个引用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>
{
}
Run Code Online (Sandbox Code Playgroud)


Jac*_*ack 14

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

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

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

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

class SandBox
{
  public static <T> void myMethod()
  {
     T foobar;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,但当然不是用main方法,因为没有办法以通用方式调用它.

编辑:问题是因为类型擦除只编译一个泛型类并传递给JVM.类型检查器只是检查代码是否安全,然后因为它证明了它被丢弃了所有类型的通用信息.

要实例化T你需要知道它的类型T,但它可以同时是多种类型,因此只需要最少量反射的一个解决方案就是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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

要与不同类型的构造函数一起操作,您必须深入挖掘反射.

  • 嗯,它在.NET中非常有意义,其中泛型在概念上与Java非常相似.唯一真正的区别是.NET没有类型擦除,这就是使得这样的代码在.NET中而不是在Java中工作的*技术*差异. (3认同)
  • Konrad,.NET具有类型推断,即使您未明确声明它也知道您在说什么,例如,IEnumerable上的许多函数都是泛型类型函数,但您通常不必指定菱形运算符因为.NET会推断其通用类型。我真的希望Java可以这么聪明。同样,在.NET中,您可以简单地调用`default(T);`或`Activator.CreateInstance(typeof(T));` (2认同)
  • @Mikepote:它们不实例化对象,它们提供存储对已创建对象的引用的功能.你不要让`ArrayList`创建一个T实例,只需存储你之前分配的T引用. (2认同)

Hos*_*Aly 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用它如下:

        Abc<String> abc = new Abc<String>(String.class);
        abc.getInstanceOfT();
Run Code Online (Sandbox Code Playgroud)

根据您的需要,您可能希望Class<? extends T>改用.

  • [`clazz.newInstance()` 自 Java 9 起已弃用](https://docs.oracle.com/javase/9​​/docs/api/java/lang/Class.html#newInstance--):_可以替换通过 `clazz.getDeclaredConstructor().newInstance()`_。 (2认同)

Bal*_*usC 5

The only way to get it to work is to use Reified Generics. And this is not supported in Java (yet? it was planned for Java 7, but has been postponed). In C# for example it is supported assuming that T has a default constructor. You can even get the runtime type by typeof(T) and get the constructors by Type.GetConstructor(). I don't do C# so the syntax may be invalid, but it roughly look like this:

public class Foo<T> where T:new() {
    public void foo() {
        T t = new T();
    }
}
Run Code Online (Sandbox Code Playgroud)

The best "workaround" for this in Java is to pass a Class<T> as method argument instead as several answers already pointed out.