我可以反射性地实例化 Java 中的泛型类型吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/356583/
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 13:46:46  来源:igfitidea点击:

can I reflectively instantiate a generic type in java?

javagenericsreflection

提问by sk.

Is it possible to reflectively instantiate a generic type in Java? Using the technique described hereI get an error because class tokens cannot be generic. Take the example below. I want to instantiate some subclass of Creator that implements Creator. The actual class name is passed in as a command line argument. The idea is to be able to specify an implementation of Creator at runtime. Is there another way to accomplish what I'm trying to do here?

是否可以在 Java 中反射性地实例化泛型类型?使用这里描述的技术我得到一个错误,因为类标记不能是通用的。以下面的例子为例。我想实例化一些实现 Creator 的 Creator 子类。实际的类名作为命令行参数传入。这个想法是能够在运行时指定 Creator 的实现。有没有另一种方法可以完成我在这里尝试做的事情?

public interface Creator<T> {
    T create();
}
public class StringCreator implements Creator<String> {
    public String create() { return new String(); }
}
public class FancyStringCreator implements Creator<String> {
    public String create() { return new StringBuffer().toString(); }
}
public static void main(String[] args) throws Exception {
    Class<?> someClass = Class.forName(args[0]);
    /*ERROR*/Class<? extends Creator<String>> creatorClass = someClass.asSubclass(Creator.class);
    Constructor<? extends Creator<String>> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
    Creator<String> creator = creatorCtor.newInstance((Object[]) null);
}

Edit: I like Marcus' approach as being the most simple and pragmatic without circumventing the whole generics thing. I can use it in my situation because I can specify that the class passed must be a subclass of StringCreator. But as Ericson pointed out the generic information is still there at the type level, just not at the runtime level so it is still possible to reflectively examine whether a given class implements the correct generic type.

编辑:我喜欢 Marcus 的方法,因为它是最简单、最务实的方法,而且没有绕过整个泛型问题。我可以在我的情况下使用它,因为我可以指定传递的类必须是 StringCreator 的子类。但是正如 Ericson 指出的那样,泛型信息仍然存在于类型级别,只是不在运行时级别,因此仍然可以反思地检查给定的类是否实现了正确的泛型类型。

采纳答案by Markus

The generic information is lost in runtime. There is no runtime equivalent of a Creator<String>.class. You could create a type between Creator and StringCreator which fixes the generic type:

通用信息在运行时丢失。没有与 Creator<String>.class 等效的运行时。您可以在 Creator 和 StringCreator 之间创建一个类型来修复泛型类型:

public interface Creator<T> {
        T create();
}
public interface StringCreator extends Creator<String> { }
public class StringCreatorImpl implements StringCreator  {
        public String create() { return new String(); }
}
public class FancyStringCreator implements StringCreator  {
        public String create() { return new StringBuffer().toString(); }
}
public static void main(String[] args) throws Exception {
        Class<?> someClass = Class.forName(args[0]);
        Class<? extends StringCreator> creatorClass = someClass.asSubclass(StringCreator.class);
        Constructor<? extends StringCreator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
        Creator<String> creator = creatorCtor.newInstance((Object[]) null);
}

But of course you lose a bit of flexibility, because you cannot use the following creator class:

但是当然你失去了一点灵活性,因为你不能使用下面的创建者类:

public class AnotherCreator implements Creator<String> {
    public String create() { return ""; }
}

回答by sblundy

You don't need that line. Nor do you need the constructor as you're just using the default one. Just instantiate the class directly:

你不需要那条线。您也不需要构造函数,因为您只是使用默认的构造函数。直接实例化类:

public static void main(String[] args) throws Exception {
        Class<?> someClass = Class.forName(args[0]);
        Creator<String> creator = (Creator<String>) someClass.newInstance();
}

If you insist, you'll only be able to get halfway there:

如果您坚持,您将只能到达一半:

public static void main(String[] args) throws Exception {
    Class<?> someClass = Class.forName(args[0]);
    Class<? extends Creator> creatorClass = someClass.asSubclass(Creator.class);
    Constructor<? extends Creator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null);
    Creator<String> creator = (Creator<String>) creatorCtor.newInstance((Object[]) null);
}

回答by Nick Holt

Not quite sure why you're using generics here.

不太确定你为什么在这里使用泛型。

The instantiation of the object using reflection would suggest a general use but presumably you're going to call createat some point and assign the result to a String, otherwise why use the generics to control the return type.

使用反射的对象实例化会建议一个通用用途,但大概你会create在某个时候调用并将结果分配给 a String,否则为什么要使用泛型来控制返回类型。

But if you wrote the following implementation of Creator:

但是如果你编写了以下 Creator 的实现:

public class IntegerCreator implements Creator<Integer> 
{
  public Integer create() 
  { 
    ...
  }
}

And passed it in as a argument you'd get a ClassCastException when calling createand assigning the result.

并将其作为参数传入,您会在调用create和分配结果时得到 ClassCastException 。

回答by erickson

This will do what you are trying to do while providing type safety. There's no way to avoid an unchecked warning, but the type checking done here justifies its suppression.

这将在提供类型安全的同时完成您正在尝试做的事情。没有办法避免未经检查的警告,但这里完成的类型检查证明了它的抑制是合理的。

  public static void main(String[] args)
    throws Exception
  {
    Class<? extends Creator<String>> clz = load(argv[0], String.class);
    Constructor<? extends Creator<String>> ctor = clz.getConstructor();
    Creator<String> creator = ctor.newInstance();
    System.out.println(creator.create());
  }

  public static <T> Class<? extends Creator<T>> load(String fqcn, Class<T> type)
    throws ClassNotFoundException
  {
    Class<?> any = Class.forName(fqcn);
    for (Class<?> clz = any; clz != null; clz = clz.getSuperclass()) {
      for (Object ifc : clz.getGenericInterfaces()) {
        if (ifc instanceof ParameterizedType) {
          ParameterizedType pType = (ParameterizedType) ifc;
          if (Creator.class.equals(pType.getRawType())) {
            if (!pType.getActualTypeArguments()[0].equals(type))
              throw new ClassCastException("Class implements " + pType);
            /* We've done the necessary checks to show that this is safe. */
            @SuppressWarnings("unchecked")
            Class<? extends Creator<T>> creator = (Class<? extends Creator<T>>) any;
            return creator;
          }
        }
      }
    }
    throw new ClassCastException(fqcn + " does not implement Creator<String>");
  }

The main restriction you have to adhere to is that a class in the hierarchy must specify the type parameter. For example class MyCreator implements Creator<String>. You can't use it with class GenericCreator<T> implements Creator<T>.

您必须遵守的主要限制是层次结构中的类必须指定类型参数。例如class MyCreator implements Creator<String>。您不能将它与class GenericCreator<T> implements Creator<T>.

It doesn't currently handle the valid case where you create a new interface interface StringCreatorIfc extends Creator<String>, and have a class implement that. It could be enhanced to do that, but I'll leave that as an exercise for those inclined.

它目前不处理您创建新 interfaceinterface StringCreatorIfc extends Creator<String>并让一个类实现它的有效情况。这样做可以得到增强,但我会把它留给那些有兴趣的人练习。