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

Posted

技术标签:

【中文标题】在Java中创建泛型类型的实例?【英文标题】:Create instance of generic type in Java? 【发布时间】:2010-09-09 15:56:40 【问题描述】:

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

class SomeContainer<E>

    E createContents()
    
        return what???
    

编辑:事实证明,Super Type Tokens 可以用来解决我的问题,但它需要大量基于反射的代码,正如下面的一些答案所示。

我将暂时搁置这个问题,看看是否有人提出与 Ian Robertson 的Artima Article 截然不同的东西。

【问题讨论】:

刚刚在 android 设备上测试了性能。 10000 次操作和:8-9 ms 采用 new SomeClass(),9-11 ms 采用 Factory.createInstance(),64-71 ms 采用最短反射:SomeClass z = SomeClass.class.newInstance()。并且所有测试都在单个 try-catch 块中。反射 newInstance() 会抛出 4 个不同的异常,还记得吗?所以我决定使用工厂模式 另见:***.com/a/5684761/59087 在 Java 8 中,您现在可以传递构造函数引用或 lambda,这使得解决这个问题变得非常简单。详情请见my answer below。 我认为编写这样的代码是个坏主意,这是解决底层问题的更优雅和可读的方法。 @DavidCitron “一会儿”他说……从那时起已经十一年了…… 【参考方案1】:

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

【讨论】:

你会如何使用反射呢?我看到的唯一方法是 Class.getTypeParameters(),但它只返回声明的类型,而不是运行时类型。 你在说这个吗? artima.com/weblogs/viewpost.jsp?thread=208860【参考方案2】:

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

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

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

【讨论】:

是的,我看到了那个解决方案,但它只有在您已经拥有对您想要实例化的类型的 Class 对象的引用时才有效。 是的,我知道。如果你可以做 E.class 那就太好了,但这只是因为擦除而给你 Object.class :) 这是解决这个问题的正确方法。这通常不是你想要的,但它就是你得到的。 那你怎么调用createContents()方法呢? 这不再是唯一的方法,现在有一种更好的方法,不需要使用 Guava 和 TypeToken 传入Class&lt;?&gt; 引用,see this answer for the code and links!【参考方案3】:

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

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

编辑:或者,您可以使用此构造函数(但它需要 E 的实例):

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

【讨论】:

是的,即使没有泛型也一样——使用泛型,这个容器的实例化变得有点多余(你必须指定“E”是两次)。 嗯,当你使用 Java 和泛型时会发生这种情况......它们并不漂亮,而且存在严重的限制......【参考方案4】:

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

public abstract class Foo<E> 

  public E instance;  

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


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

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

但这需要做很多工作,而且只适用于子类。不过可以很方便。

【讨论】:

是的,这很好,特别是如果泛型类是抽象的,你可以在具体的子类中这样做:) 如果类Foo 不是抽象的,此方法也可以工作。但为什么它只适用于 Foo 的匿名子类?假设我们将Foo具体化(我们省略了abstract),为什么new Foo&lt;Bar&gt;();会导致错误,而new Foo&lt;Bar&gt;();不会? (例外:“类不能转换为 ParameterizedType”) @TimKuipers class Foo&lt;E&gt; 中的 &lt;E&gt; 未绑定到任何特定类型。只要E 没有静态 绑定,您就会看到异常行为,例如:new Foo&lt;Bar&gt;()new Foo&lt;T&gt;() ...class Fizz &lt;E&gt; extends Foo&lt;E&gt;。第一种情况不是静态绑定的,它在编译时被擦除。第二种情况用另一个类型变量 (T) 代替 E,但仍然未绑定。在最后一种情况下,很明显E 仍然是未绑定的。 静态绑定类型参数的一个例子是class Fizz extends Foo&lt;Bar&gt;——在这种情况下,Fizz 的用户会得到一个Foo&lt;Bar&gt; 并且只能是Foo&lt;Bar&gt;。因此,在这种情况下,编译器很乐意将该信息编码到 Fizz 的类元数据中,并将其作为 ParameterizedType 提供给反射代码。当你创建一个像new Foo&lt;Bar&gt;() ... 这样的匿名内部类时,它会做同样的事情,除了Fizz 之外,编译器会生成一个“匿名”类名,在编译外部类之前你不会知道。 需要注意的是,如果类型参数也是 ParameterizedType,这将不起作用。例如,Foo&lt;Bar&lt;Baz&gt;&gt;。您将创建一个无法显式创建的 ParameterizedTypeImpl 实例。因此,检查getActualTypeArguments()[0] 是否返回ParameterizedType 是个好主意。如果是,那么您想获取原始类型,然后创建一个实例。【参考方案5】:

您需要某种抽象工厂来将责任推卸给:

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

【讨论】:

..Factory.create() 是什么样子的? @OhadR Factory&lt;&gt; 是一个接口,所以没有正文。关键是您需要一个间接层来将责任转嫁给那些“知道”构造实例所需的代码的方法。使用普通代码而不是元语言 ClassConstructor 这样做要好得多,因为反射会带来整个世界的伤害。 现在您可以使用如下方法引用表达式创建工厂实例:SomeContainer&lt;SomeElement&gt; cont = new SomeContainer&lt;&gt;(SomeElement::new);【参考方案6】:

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

interface Factory<E>
    E create();
    

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

【讨论】:

【参考方案7】:

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

new SomeContainer<SomeType>(SomeType.class);

你可以使用工厂方法:

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

喜欢:

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


【讨论】:

【参考方案8】:

你可以使用:

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

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

【讨论】:

你如何在运行时获得泛型类型的确切类名? 您必须使用该类的实例来保存它。可行,虽然不太方便。如果您的泛型有 E 类型(或 T 或其他类型)的成员,则获取它的二进制名称只是 foo.getClass().getName()。那个实例来自哪里?我目前正在将一个传递给我正在处理的项目中的构造函数。【参考方案9】:
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());
    


【讨论】:

如果您在泛型中使用泛型类型,则此代码的好方法可能会导致 ClassCastException。然后您检索实际类型参数您应该检查它是否也是 ParamterizedType,如果是,则返回他的 RawType(或比这更好的东西)。另一个问题是,当我们扩展更多的时候,这段代码也会抛出 ClassCastExeption。 原因:java.lang.ClassCastException:sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 无法转换为 java.lang.Class @DamianLeszczyński-Vash 也会失败,例如class GenericHome&lt;T&gt; extends Home&lt;T&gt;【参考方案10】:
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();

【讨论】:

这在我的原始问题示例中不起作用。 SomeContainer 的超类就是 Object。因此,this.getClass().getGenericSuperclass() 返回 Class(类 java.lang.Object),而不是 ParameterizedType。这实际上已经被同行回答***.com/questions/75175/… 指出了。 完全错误:线程“main”中的异常 java.lang.ClassCastException:java.lang.Class 无法转换为 java.lang.reflect.ParameterizedType【参考方案11】:

您可以使用以下 sn-p 实现此目的:

import java.lang.reflect.ParameterizedType;

public class SomeContainer<E> 
   E createContents() throws InstantiationException, IllegalAccessException 
      ParameterizedType genericSuperclass = (ParameterizedType)
         getClass().getGenericSuperclass();
      @SuppressWarnings("unchecked")
      Class<E> clazz = (Class<E>)
         genericSuperclass.getActualTypeArguments()[0];
      return clazz.newInstance();
   
   public static void main( String[] args ) throws Throwable 
      SomeContainer< Long > scl = new SomeContainer<>();
      Long l = scl.createContents();
      System.out.println( l );
   

【讨论】:

完全错误:线程“main”中的异常 java.lang.ClassCastException:java.lang.Class 无法转换为 java.lang.reflect.ParameterizedType【参考方案12】:

来自Java Tutorial - Restrictions on Generics:

Cannot Create Instances of Type Parameters

您不能创建类型参数的实例。例如,以下代码会导致编译时错误:

public static <E> void append(List<E> list) 
    E elem = new E();  // compile-time error
    list.add(elem);

作为一种解决方法,您可以通过反射创建一个类型参数的对象:

public static <E> void append(List<E> list, Class<E> cls) throws Exception 
    E elem = cls.getDeclaredConstructor().newInstance();   // OK
    list.add(elem);

您可以按如下方式调用 append 方法:

List<String> ls = new ArrayList<>();
append(ls, String.class);

【讨论】:

cls.newInstance() 已贬值以支持cls.getDeclaredConstructor().newInstance() @antikbd 感谢您的提示!我已经相应地更新了示例。【参考方案13】:

我以为我能做到,但很失望:它不起作用,但我认为它仍然值得分享。

也许有人可以纠正:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeContainer<E> 
    E createContents();


public class Main 

    @SuppressWarnings("unchecked")
    public static <E> SomeContainer<E> createSomeContainer() 
        return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class[] SomeContainer.class , new InvocationHandler() 
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
                Class<?> returnType = method.getReturnType();
                return returnType.newInstance();
            
        );
    

    public static void main(String[] args) 
        SomeContainer<String> container = createSomeContainer();

    [*] System.out.println("String created: [" +container.createContents()+"]");

    

它产生:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

第 26 行是带有[*] 的行。

唯一可行的解​​决方案是@JustinRudd 提出的解决方案

【讨论】:

【参考方案14】:

如果你需要一个泛型类中类型参数的新实例,那么让你的构造函数要求它的类...

public final class Foo<T> 

    private Class<T> typeArgumentClass;

    public Foo(Class<T> typeArgumentClass) 

        this.typeArgumentClass = typeArgumentClass;
    

    public void doSomethingThatRequiresNewT() throws Exception 

        T myNewT = typeArgumentClass.newInstance();
        ...
    

用法:

Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);

优点:

比 Robertson 的超级类型令牌 (STT) 方法简单得多(问题更少)。 比 STT 方法(早餐会吃掉您的手机)效率更高。

缺点:

无法将 Class 传递给默认构造函数(这就是 Foo 为 final 的原因)。如果您确实需要一个默认构造函数,您可以随时添加一个 setter 方法,但您必须记得稍后再给她一个调用。 Robertson 的反对意见... 比害群之马还多的酒吧(尽管再次指定类型参数类不会完全杀死你)。与 Robertson 的说法相反,这并不违反 DRY 原则,因为编译器将确保类型正确性。 不完全是Foo&lt;L&gt;proof。对于初学者...如果类型参数类没有默认构造函数,newInstance() 将抛出一个 wobbler。无论如何,这确实适用于所有已知的解决方案。 缺乏对 STT 方法的完全封装。不过没什么大不了的(考虑到 STT 的惊人性能开销)。

【讨论】:

【参考方案15】:

你可以用一个类加载器和类名,最后加上一些参数。

final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);

【讨论】:

这比只传递类对象更糟糕 您根本不需要显式引用类加载器。【参考方案16】:

当您在编译时使用 E 时,您并不真正关心实际的泛型类型“E”(您使用反射或使用泛型类型的基类),因此让子类提供 E 的实例。

abstract class SomeContainer<E>

    abstract protected E createContents();
    public void doWork()
        E obj = createContents();
        // Do the work with E 
     


class BlackContainer extends SomeContainer<Black>
    protected Black createContents() 
        return new Black();
    

【讨论】:

我喜欢这种方法,因为它可读且不会带来任何施法魔法。缺点是您必须在每个派生类中实现 createContents。即使你不需要它。另一种方法是使 createContents 不是抽象的,而是使用空实现(返回 null/throws)......在这种情况下,您只能在需要时实现它。【参考方案17】:

有各种库可以使用类似于 Robertson 文章讨论的技术为您解析 E。这是createContents 的一个实现,它使用TypeTools 来解析由E 表示的原始类:

E createContents() throws Exception 
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();

这假定 getClass() 解析为 SomeContainer 的子类,否则将失败,因为如果 E 的实际参数化值未在子类中捕获,则在运行时将被删除。

【讨论】:

【参考方案18】:

您现在就可以做到这一点,它不需要一堆反射代码。

import com.google.common.reflect.TypeToken;

public class Q26289147

    public static void main(final String[] args) throws IllegalAccessException, InstantiationException
    
        final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() ;
        final String string = (String) smpc.type.getRawType().newInstance();
        System.out.format("string = \"%s\"",string);
    

    static abstract class StrawManParameterizedClass<T>
    
        final TypeToken<T> type = new TypeToken<T>(getClass()) ;
    

当然,如果您需要调用需要一些反射的构造函数,但这是有据可查的,那么这个技巧就不是了!

这里是JavaDoc for TypeToken。

【讨论】:

此解决方案适用于有限的情况,就像@noah 的反思回答一样。我今天都尝试了它们......最后我将参数类的实例传递给参数化类(以便能够调用 .newInstance() )。 “泛型”的很大缺陷... new Foo(Bar.class); ... class Foo private final Class mTFactory; Foo(Class tClass) mTFactory = tClass; ... T 实例 = tFactory.newInstance(); 这适用于所有情况,即使是采用泛型参数的静态工厂方法【参考方案19】:

@Noah 回答的改进。

改变的原因

a]如果您更改订单时使用超过 1 种泛型类型会更安全。

b] 类泛型类型签名会不时更改,因此您不会对运行时中无法解释的异常感到惊讶。

健壮的代码

public abstract class Clazz<P extends Params, M extends Model> 

    protected M model;

    protected void createModel() 
    Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
    for (Type type : typeArguments) 
        if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) 
            try 
                model = ((Class<M>) type).newInstance();
             catch (InstantiationException | IllegalAccessException e) 
                throw new RuntimeException(e);
            
        
    

或者使用一个衬垫

一行代码

model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();

【讨论】:

【参考方案20】:

这是createContents 的一个实现,它使用TypeTools 来解析E 表示的原始类:

E createContents() throws Exception 
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();

此方法仅在 SomeContainer 被子类化时才有效,因此 E 的实际值在类型定义中被捕获:

class SomeStringContainer extends SomeContainer<String>

否则 E 的值会在运行时被擦除且不可恢复。

【讨论】:

【参考方案21】:

考虑一种更实用的方法:不要无中生有地创建一些 E(这显然是代码异味),而是传递一个知道如何创建 E 的函数,即

E createContents(Callable<E> makeone) 
     return makeone.call(); // most simple case clearly not that useful

【讨论】:

从技术上讲,您不是在传递一个函数,而是在传递一个 函数对象(也称为 functor)。 或者为了克服捕捉Exception 使用Supplier&lt;E&gt; 代替。【参考方案22】:

不幸的是,Java 不允许您做您想做的事情。见official workaround:

您不能创建类型参数的实例。例如,以下代码会导致编译时错误:

public static <E> void append(List<E> list) 
    E elem = new E();  // compile-time error
    list.add(elem);

作为一种解决方法,您可以通过反射创建一个类型参数的对象:

public static <E> void append(List<E> list, Class<E> cls) throws Exception 
    E elem = cls.newInstance();   // OK
    list.add(elem);

您可以按如下方式调用 append 方法:

List<String> ls = new ArrayList<>();
append(ls, String.class);

【讨论】:

你能告诉我为什么你这样做的时候会反对吗?我不明白为什么官方的解决方法是一个糟糕的解决方案。谢谢。 我猜你会投反对票,因为你的答案与陆克文的基本相同:***.com/a/75254/103412【参考方案23】:

在 Java 8 中,您可以使用 Supplier 函数式接口非常轻松地实现这一点:

class SomeContainer<E> 
  private Supplier<E> supplier;

  SomeContainer(Supplier<E> supplier) 
    this.supplier = supplier;
  

  E createContents() 
    return supplier.get();
  

你可以这样构造这个类:

SomeContainer<String> stringContainer = new SomeContainer<>(String::new);

该行上的语法String::new 是constructor reference。

如果您的构造函数接受参数,您可以使用 lambda 表达式:

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));

【讨论】:

好一个。它避免了反射和必须处理异常。 太好了。不幸的是,对于 Android 用户,这需要 API 级别 24 或更高。 ...这与this even older answer 没有什么不同,表明它背后的技术模式甚至比 Java 对 lambda 表达式和方法引用的支持还要古老,而您甚至可以在升级后将旧代码与它们一起使用编译器… 可以只输入SomeContainer stringContainer = new SomeContainer(String::new);吗? @AaronFranke:不,因为那样你会使用raw type。【参考方案24】:

这是一个改进的解决方案,基于@noah、@Lars Bohl 和其他一些人已经提到的ParameterizedType.getActualTypeArguments

实施中的第一个小改进。工厂不应该返回实例,而是一个类型。一旦您使用Class.newInstance() 返回实例,您就会减少使用范围。因为只有无参数的构造函数可以像这样被调用。更好的方法是返回一个类型,并允许客户端选择他想要调用的构造函数:

public class TypeReference<T> 
  public Class<T> type()
    try 
      ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
      if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0)
        throw new IllegalStateException("Could not define type");
      
      if (pt.getActualTypeArguments().length != 1)
        throw new IllegalStateException("More than one type has been found");
      
      Type type = pt.getActualTypeArguments()[0];
      String typeAsString = type.getTypeName();
      return (Class<T>) Class.forName(typeAsString);

     catch (Exception e)
      throw new IllegalStateException("Could not identify type", e);
    

  

这是一个用法示例。 @Lars Bohl 仅展示了一种通过扩展获得具体化的通用方法。 @noah 只能通过使用 创建一个实例。以下是演示这两种情况的测试:

import java.lang.reflect.Constructor;

public class TypeReferenceTest 

  private static final String NAME = "Peter";

  private static class Person
    final String name;

    Person(String name) 
      this.name = name;
    
  

  @Test
  public void erased() 
    TypeReference<Person> p = new TypeReference<>();
    Assert.assertNotNull(p);
    try 
      p.type();
      Assert.fail();
     catch (Exception e)
      Assert.assertEquals("Could not identify type", e.getMessage());
    
  

  @Test
  public void reified() throws Exception 
    TypeReference<Person> p = new TypeReference<Person>();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  

  static class TypeReferencePerson extends TypeReference<Person>

  @Test
  public void reifiedExtenension() throws Exception 
    TypeReference<Person> p = new TypeReferencePerson();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  

注意:您可以强制TypeReference 的客户端在创建实例时始终使用,方法是使此类抽象:public abstract class TypeReference&lt;T&gt;。我没有这样做,只是为了显示已擦除的测试用例。

【讨论】:

【参考方案25】:

希望现在帮上忙还为时不晚!!!

Java 是类型安全的,这意味着只有对象才能创建实例。

就我而言,我无法将参数传递给createContents 方法。我的解决方案是使用与下面的答案不同的扩展。

private static class SomeContainer<E extends Object> 
    E e;
    E createContents() throws Exception
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    

这是我无法传递参数的示例。

public class SomeContainer<E extends Object> 
    E object;

    void resetObject throws Exception
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    

如果您使用无对象类型扩展泛型类,则使用反射会创建运行时错误。要将您的泛型类型扩展为对象,请将此错误转换为编译时错误。

【讨论】:

【参考方案26】:

你能做的是-

    首先声明那个泛型类的变量

    2.然后创建它的构造函数并实例化该对象

    然后在你想用的地方用它

例子-

1

private Class&lt;E&gt; entity;

2

public xyzservice(Class<E> entity) 
        this.entity = entity;
    



public E getEntity(Class<E> entity) throws InstantiationException, IllegalAccessException 
        return entity.newInstance();
    

3.

E e = getEntity(实体);

【讨论】:

返回 entity.newInstance();触发警告:“Class 类型的方法 newInstance() 自版本 9 起已弃用”【参考方案27】:

使用TypeToken&lt;T&gt; 类:

public class MyClass<T> 
    public T doSomething() 
        return (T) new TypeToken<T>().getRawType().newInstance();
    

【讨论】:

如果你使用 Guava 而不是 GSON,那就有点不同了:(T) new TypeToken&lt;T&gt;(getClass()).getRawType().newInstance();【参考方案28】:

请注意,kotlin 中的泛型类型可能没有默认构造函数。

 implementation("org.objenesis","objenesis", "3.2")

    val fooType = Foo::class.java
    var instance: T = try 
        fooType.newInstance()
     catch (e: InstantiationException) 
//            Use Objenesis because the fooType class has not a default constructor
        val objenesis: Objenesis = ObjenesisStd()
        objenesis.newInstance(fooType)
    
Withou default constructor Objenesis

【讨论】:

【参考方案29】:

Ira 的解决方案启发了我,并对其稍作修改。

abstract class SomeContainer<E>

    protected E createContents() 
        throw new NotImplementedException();
    

    public void doWork()
        E obj = createContents();
        // Do the work with E 
     


class BlackContainer extends SomeContainer<Black>
    // this method is optional to implement in case you need it
    protected Black createContents() 
        return new Black();
    

如果您需要 E 实例,您可以在派生类中实现 createContents 方法(或者在您不需要时不实现它。

【讨论】:

以上是关于在Java中创建泛型类型的实例?的主要内容,如果未能解决你的问题,请参考以下文章

通过未经检查的类型转换在 Java 中创建泛型数组

我无法在 Java 中创建泛型数组类型的原因是啥?

如何在颤振/飞镖中创建泛型类型的对象?

Java泛型

JAVA中的泛型类是啥东西?

java 方法中如何在返回类型使用泛型