在 Java 中创建泛型类型的实例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/75175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:59:22  来源:igfitidea点击:

Create instance of generic type in Java?

javagenerics

提问by David Citron

Is it possible to create an instance of a generic type in Java? I'm thinking based on what I've seen that the answer is no(due to type erasure), but I'd be interested if anyone can see something I'm missing:

是否可以在 Java 中创建泛型类型的实例?我正在考虑根据我所看到的答案是no由于类型擦除),但如果有人能看到我遗漏的东西,我会很感兴趣:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

EDIT: It turns out that Super Type Tokenscould be used to resolve my issue, but it requires a lot of reflection-based code, as some of the answers below have indicated.

编辑:事实证明,超级类型令牌可用于解决我的问题,但它需要大量基于反射的代码,如下面的一些答案所示。

I'll leave this open for a little while to see if anyone comes up with anything dramatically different than Ian Robertson's Artima Article.

我将把这个开放一段时间,看看是否有人提出与 Ian Robertson 的Artima 文章截然不同的内容

回答by Adam Rosenfield

As you said, you can't really do it because of type erasure. You can sort of do it using reflection, but it requires a lot of code and lot of error handling.

正如您所说,由于类型擦除,您无法真正做到这一点。您可以使用反射来完成它,但它需要大量代码和大量错误处理。

回答by Justin Rudd

You are correct. You can't do new E(). But you can change it to

你是对的。你不能做new E()。但是你可以把它改成

private static class SomeContainer<E> {
    E createContents(Class<E> clazz) {
        return clazz.newInstance();
    }
}

It's a pain. But it works. Wrapping it in the factory pattern makes it a little more tolerable.

这是一种痛苦。但它有效。将它包装在工厂模式中使其更容易忍受。

回答by Mike Stone

Here is an option I came up with, it may help:

这是我想出的一个选项,它可能会有所帮助:

public static class Container<E> {
    private Class<E> clazz;

    public Container(Class<E> clazz) {
        this.clazz = clazz;
    }

    public E createContents() throws Exception {
        return clazz.newInstance();
    }
}

EDIT: Alternatively you can use this constructor (but it requires an instance of E):

编辑:或者你可以使用这个构造函数(但它需要一个 E 的实例):

@SuppressWarnings("unchecked")
public Container(E instance) {
    this.clazz = (Class<E>) instance.getClass();
}

回答by noah

Dunno if this helps, but when you subclass (including anonymously) a generic type, the type information is available via reflection. e.g.,

不知道这是否有帮助,但是当您子类化(包括匿名)泛型类型时,类型信息可通过反射获得。例如,

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

So, when you subclass Foo, you get an instance of Bar e.g.,

所以,当你继承 Foo 时,你会得到一个 Bar 的实例,例如,

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

But it's a lot of work, and only works for subclasses. Can be handy though.

但这需要大量工作,并且仅适用于子类。不过可以很方便。

回答by Tom Hawtin - tackline

You'll need some kind of abstract factory of one sort or another to pass the buck to:

你需要某种抽象工厂来推卸责任:

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

回答by Pavel Feldman

If you mean new E()then it is impossible. And I would add that it is not always correct - how do you know if E has public no-args constructor? But you can always delegate creation to some other class that knows how to create an instance - it can be Class<E>or your custom code like this

如果你的意思是 new E()那是不可能的。我要补充一点,它并不总是正确的——你怎么知道 E 是否有公共无参数构造函数?但是您始终可以将创建委托给其他知道如何创建实例的类 - 它可以是这样的Class<E>或您的自定义代码

interface Factory<E>{
    E create();
}    

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}

回答by jb.

If you want not to type class name twice during instantiation like in:

如果您不想在实例化过程中两次键入类名,例如:

new SomeContainer<SomeType>(SomeType.class);

You can use factory method:

您可以使用工厂方法:

<E> SomeContainer<E> createContainer(Class<E> class); 

Like in:

像:

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

}

回答by jb.

You can use:

您可以使用:

Class.forName(String).getConstructor(arguments types).newInstance(arguments)

But you need to supply the exact class name, including packages, eg. java.io.FileInputStream. I used this to create a math expressions parser.

但是您需要提供确切的类名,包括包,例如。java.io.FileInputStream. 我用它来创建一个数学表达式解析器。

回答by Lars Bohl

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

}

回答by Rachid

return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();