简体   繁体   English

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

[英]can I reflectively instantiate a generic type in java?

Is it possible to reflectively instantiate a generic type in Java? 是否可以在Java中以反射方式实例化泛型? Using the technique described here I get an error because class tokens cannot be generic. 使用此处描述的技术,由于类标记不能通用,因此会出现错误。 Take the example below. 请看下面的例子。 I want to instantiate some subclass of Creator that implements Creator. 我想实例化一些实现Creator的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. 这个想法是为了能够在运行时指定Creator的实现。 Is there another way to accomplish what I'm trying to do here? 还有另一种方法可以完成我在这里要做的事情吗?

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. 编辑:我喜欢Marcus的方法,因为它是最简单,最实用的方法,不会绕过整个泛型的东西。 I can use it in my situation because I can specify that the class passed must be a subclass of StringCreator. 我可以在我的情况下使用它,因为我可以指定所传递的类必须是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. 但是,正如Ericson指出的那样,泛型信息仍然存在于类型级别,而不仅仅是运行时级别,因此仍然有可能反思性地检查给定的类是否实现了正确的泛型类型。

The generic information is lost in runtime. 通用信息在运行时丢失。 There is no runtime equivalent of a Creator<String>.class. 没有等效于Creator <String> .class的运行时。 You could create a type between Creator and StringCreator which fixes the generic type: 您可以在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 ""; }
}

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. 您必须遵守的主要限制是层次结构中的类必须指定type参数。 For example class MyCreator implements Creator<String> . 例如, class MyCreator implements Creator<String> You can't use it with class GenericCreator<T> implements Creator<T> . 您不能将其与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. 当前,当您创建一个新接口interface StringCreatorIfc extends Creator<String>interface StringCreatorIfc extends Creator<String>实现的类时,它无法处理这种情况。 It could be enhanced to do that, but I'll leave that as an exercise for those inclined. 可以对此进行增强,但是我将其保留为那些倾向的人的练习。

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

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 create at some point and assign the result to a String , otherwise why use the generics to control the return type. 使用反射对对象进行实例化将建议一般使用,但是大概您将在某个时候调用create并将结果分配给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 create and assigning the result. 并将其作为参数传递给您,在调用create并分配结果时,您将获得ClassCastException。

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

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