【问题标题】:Create instance of generic type in Java?在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 个不同的异常,还记得吗?所以我决定使用工厂模式
  • 在 Java 8 中,您现在可以传递构造函数引用或 lambda,这使得解决这个问题变得非常简单。详情请见my answer below
  • 我认为编写这样的代码是个坏主意,这是解决底层问题的更优雅和可读的方法。
  • @DavidCitron “一会儿”他说……从那时起已经十一年了……

标签: java generics


【解决方案1】:

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

【讨论】:

【解决方案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。这实际上已经被同行回答stackoverflow.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);
                

                【讨论】:

                • 你能告诉我为什么你这样做的时候会反对吗?我不明白为什么官方的解决方法是一个糟糕的解决方案。谢谢。
                • 我猜你会投反对票,因为你的答案与陆克文的基本相同:stackoverflow.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::newconstructor 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】:

                    你能做的是-

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

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

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

                    【讨论】:

                      【解决方案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 方法(或者在您不需要时不实现它。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2014-05-31
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2023-04-01
                        • 2013-09-18
                        相关资源
                        最近更新 更多