简体   繁体   English

在java中实例化泛型类型

[英]Instantiating generics type in java

I would like to create an object of Generics Type in java.我想在java中创建一个泛型类型的对象。 Please suggest how can I achieve the same.请建议我如何实现相同的目标。

Note: This may seem a trivial Generics Problem.注意:这似乎是一个微不足道的泛型问题。 But I bet.. it isn't.但我敢打赌……不是。 :) :)

suppose I have the class declaration as:假设我的类声明为:

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

You'll have to add exception handling.您必须添加异常处理。

You have to pass the actual type at runtime, since it is not part of the byte code after compilation, so there is no way to know it without explicitly providing it.您必须在运行时传递实际类型,因为它不是编译后字节码的一部分,因此如果不明确提供它,就无法知道它。

In the code you posted, it's impossible to create an instance of T since you don't know what type that is:在您发布的代码中,无法创建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
       }
} 

Of course it is possible if you have a reference to Class<T> and T has a default constructor, just call newInstance() on the Class object.当然,如果您有对Class<T>的引用并且T具有默认构造函数,则只需在Class对象上调用newInstance()

If you subclass Abc<T> you can even work around the type erasure problem and won't have to pass any Class<T> references around:如果您子类化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>
{
}

What you wrote doesn't make any sense, generics in Java are meant to add the functionality of parametric polymorphism to objects.您写的内容没有任何意义,Java 中的泛型旨在为对象添加参数多态性的功能。

What does it mean?这是什么意思? It means that you want to keep some type variables of your classes undecided, to be able to use your classes with many different types.这意味着您希望保持类的某些类型变量未定,以便能够使用具有许多不同类型的类。

But your type variable T is an attribute that is resolved at run-time, the Java compiler will compile your class proving type safety without trying to know what kind of object is T so it's impossible for it to let your use a type variable in a static method.但是你的型变T的是,在运行时被解析的属性,Java编译器将编译类,证明类的安全,但不尝试知道什么样的对象是T所以不可能为它让你使用一个类型变量静态方法。 The type is associated to a run-time instance of the object while public void static main(..) is associated to the class definition and at that scope T doesn't mean anything.该类型与对象的运行时实例相关联,而public void static main(..)与类定义相关联,并且在该范围内T没有任何意义。

If you want to use a type variable inside a static method you have to declare the method as generic (this because, as explained type variables of a template class are related to its run-time instance ), not the class:如果您想在静态方法中使用类型变量,您必须将该方法声明为泛型(这是因为,正如所解释的,模板类的类型变量与其运行时实例相关),而不是类:

class SandBox
{
  public static <T> void myMethod()
  {
     T foobar;
  }
}

this works, but of course not with main method since there's no way to call it in a generic way.这有效,但当然不适用于main方法,因为无法以通用方式调用它。

EDIT : The problem is that because of type erasure just one generic class is compiled and passed to JVM.编辑:问题在于,由于类型擦除,只有一个泛型类被编译并传递给 JVM。 Type checker just checks if code is safe, then since it proved it every kind of generic information is discarded.类型检查器只是检查代码是否安全,然后因为它证明了它,所以所有类型的通用信息都被丢弃了。

To instantiate T you need to know the type of T , but it can be many types at the same time, so one solution with requires just the minimum amount of reflection is to use Class<T> to instantiate new objects:要实例化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());
    }
}

Of course this implies that the type you want to instantiate:当然,这意味着您要实例化的类型:

  • is not a primitive type不是原始类型
  • it has a default constructor它有一个默认构造函数

To operate with different kind of constructors you have to dig deeper into reflection.要使用不同类型的构造函数,您必须深入研究反射。

You need to get the type information statically.您需要静态获取类型信息。 Try this:尝试这个:

public class Abc<T> {
    private Class<T> clazz;

    public Abc(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T getInstanceOfT()
            throws throws InstantiationException,
                 IllegalAccessException,
                 IllegalArgumentException,
                 InvocationTargetException,
                 NoSuchMethodException,
                 SecurityException {
        return clazz.getDeclaredConstructor().newInstance();
    }
}

Use it as such:像这样使用它:

        Abc<String> abc = new Abc<String>(String.class);
        abc.getInstanceOfT();

Depending on your needs, you may want to use Class<? extends T>根据您的需要,您可能希望使用Class<? extends T> Class<? extends T> instead.改为Class<? extends T>

The only way to get it to work is to useReified Generics .让它工作的唯一方法是使用Reified 泛型 And this is not supported in Java (yet? it was planned for Java 7, but has been postponed).这在 Java 中不受支持(但是?它计划用于 Java 7,但已被推迟)。 In C# for example it is supported assuming that T has a default constructor.例如,在 C# 中,假设T具有默认构造函数,则支持它。 You can even get the runtime type by typeof(T) and get the constructors by Type.GetConstructor() .您甚至可以通过typeof(T)获取运行时类型并通过Type.GetConstructor()获取构造函数。 I don't do C# so the syntax may be invalid, but it roughly look like this:我不使用 C#,因此语法可能无效,但大致如下所示:

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

The best "workaround" for this in Java is to pass a Class<T> as method argument instead as several answers already pointed out. Java 中最好的“解决方法”是将Class<T>作为方法参数传递,而不是像已经指出的几个答案。

First of all, you can't access the type parameter T in the static main method, only on non-static class members (in this case).首先,您不能在静态 main 方法中访问类型参数T ,只能在非静态类成员上访问(在这种情况下)。

Second, you can't instantiate T because Java implements generics with Type Erasure .其次,您无法实例化T因为 Java 使用Type Erasure实现泛型。 Almost all the generic information is erased at compile time.几乎所有的通用信息在编译时都会被删除。

Basically, you can't do this:基本上,你不能这样做:

T member = new T();

Here's a nice tutorial on generics .这是一个关于 泛型的很好的教程。

You don't seem to understand how Generics work.你似乎不明白泛型是如何工作的。 You may want to look at http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html Basically what you could do is something like你可能想看看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();
  }

} 

I was implementing the same using the following approach.我正在使用以下方法实现相同的功能。

public class Abc<T>
{
   T myvar;

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

I was trying to find a better way to achieve the same.我试图找到一种更好的方法来实现同样的目标。

Isn't it possible?不可能吗?

Type Erasure Workaround类型擦除解决方法

Inspired by @martin's answer , I wrote a helper class that allows me to workaround the type erasure problem.受到@martin's answer 的启发,我编写了一个帮助类,它允许我解决类型擦除问题。 Using this class (and a little ugly trick) I'm able to create a new instance out of a template type:使用这个类(还有一个丑陋的技巧),我可以从模板类型中创建一个新实例:

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

The ugly trick here is to make the class abstract so the user of the class is forced to subtype it.这里的丑陋技巧是使类abstract因此类的用户被迫对它进行子类型化。 Here I'm subclassing it by appending {} after the call to the constructor.在这里,我通过在调用构造函数之后附加{}对其进行子类化。 This defines a new anonymous class and creates an instance of it.这定义了一个新的匿名类并创建了它的一个实例。

Once the generic class is subtyped with concrete template types, I'm able to retrieve the template 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 );
        }
    }
}

There are hacky ways around this when you really have to do it.当你真的必须这样做时,有一些技巧可以解决这个问题。

Here's an example of a transform method that I find very useful;这是我发现非常有用的转换方法的示例; and provides one way to determine the concrete class of a generic.并提供了一种确定泛型的具体类的方法。

This method accepts a collection of objects as input, and returns an array where each element is the result of calling a field getter on each object in the input collection.此方法接受对象集合作为输入,并返回一个数组,其中每个元素都是对输入集合中的每个对象调用字段 getter 的结果。 For example, say you have a List<People> and you want a String[] containing everyone's last name.例如,假设您有一个List<People>并且您想要一个String[]包含每个人的姓氏。

The type of the field value returned by the getter is specified by the generic E , and I need to instantiate an array of type E[] to store the return value. getter 返回的字段值的类型由泛型E指定,我需要实例化一个E[]类型的数组来存储返回值。

The method itself is a bit ugly, but the code you write that uses it can be so much cleaner.该方法本身有点难看,但是您编写的使用它的代码可以更简洁。

Note that this technique only works when somewhere in the input arguments there is an object whose type matches the return type, and you can deterministically figure it out.请注意,此技术仅在输入参数中某处存在类型与返回类型匹配的对象时才有效,并且您可以确定性地找出它。 If the concrete classes of your input parameters (or their sub-objects) can tell you nothing about the generics, then this technique won't work.如果您的输入参数(或其子对象)的具体类不能告诉您关于泛型的任何信息,那么这种技术将不起作用。

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

Full code is here: https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28完整代码在这里: https : //github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28

It looks like you are trying to create the class that serves as the entry point to your application as a generic, and that won't work... The JVM won't know what type it is supposed to be using when it's instantiated as you start the application.看起来您正在尝试创建作为泛型应用程序入口点的类,但这是行不通的……JVM 在实例化时不知道它应该使用什么类型你启动应用程序。

However, if this were the more general case, then something like would be what you're looking for:但是,如果这是更一般的情况,那么您正在寻找的内容如下:

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

or perhaps:也许:

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

..例如

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM