【问题标题】:can I reflectively instantiate a generic type in java?我可以在 java 中反射性地实例化一个泛型类型吗?
【发布时间】:2008-12-10 16:01:47
【问题描述】:

是否可以在 Java 中反射地实例化泛型类型?使用here 描述的技术我得到一个错误,因为类标记不能是通用的。举个例子。我想实例化一些实现 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);
}

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

【问题讨论】:

    标签: java generics reflection


    【解决方案1】:

    通用信息在运行时丢失。没有 Creator.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);
    }
    

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

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

    【讨论】:

    • 并非所有通用信息在运行时都会丢失;即使没有创建另一个接口,也有足够的余量来检查必要的类型信息。只是没有足够的信息让编译器推断正确性。
    • 你就在那里。我所说的(以一种复杂的方式)是没有代表 Creator 与 Creator 不同的类对象。但是您在下面截断的代码似乎是解决问题的合理方法。
    【解决方案2】:

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

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

    您必须遵守的主要限制是层次结构中的类必须指定类型参数。例如class MyCreator implements Creator&lt;String&gt;。您不能将它与class GenericCreator&lt;T&gt; implements Creator&lt;T&gt; 一起使用。

    它目前不处理创建新接口interface StringCreatorIfc extends Creator&lt;String&gt; 并让类实现它的有效情况。这样做可以增强,但我会把它留给那些有兴趣的人练习。

    【讨论】:

      【解决方案3】:

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

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

      如果你坚持,你只能走到一半:

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

      【讨论】:

      • claz.newInstance() 和 ctor.newInstance() 在发生错误时的行为并不完全相同。类的 newInstance 方法较旧,并且与其他反射“方法”调用不一致。
      【解决方案4】:

      不太清楚你为什么在这里使用泛型。

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-18
        • 2010-11-08
        相关资源
        最近更新 更多