【问题标题】:Instantiating type with generics dynamically使用泛型动态实例化类型
【发布时间】:2012-09-23 07:21:57
【问题描述】:

我猜我绝对不可能做这样的事情:

Class c = Class.forName("Processor<Integer, String>");

在 Java 中? (当然,我之前定义了处理器)。

【问题讨论】:

  • 我认为这里更好的问题是,你为什么认为你需要这样做?也许,有一种不涉及反射的更简单的方法。
  • 好的,我确实有一个潜在的用例,它可能还有其他解决方案。我确实有一个处理器类,其功能我想为不同的输入重用,其中输入是从 Avro 模式生成的。我想用不同的、兼容的输入模式调用同一个处理器。
  • 可能有一些方法可以通过reflection API 完成类似的事情,但不清楚这是否会很简单。您能否更清楚地说明为什么 Java 的继承模型(以及多态性或其他东西)没有涵盖您想要做的任何事情?另外,您能否详细说明您的问题,以便我们了解您的目标?
  • 是的,各种输入类型之间的继承会很好,但我没有。其中一部分是遗产。另一个原因是我们应该支持从 Avro 模式生成的原始类型和更复杂的类型,并且我们不想包装每个 int/String...。此外,我们已经有了处理器的配置机制,如果用户可以重用相同的处理器逻辑,而只是改变配置文件中的实际(兼容)输入类型,那就太好了。

标签: java generics reflection


【解决方案1】:

绝对不可能,因为泛型参数只能在编译时存在。 Class 对象在运行时是相同的。它不是 C++ 中的类模板。类型参数只是 java 编译器的信息。

你可以尝试通过创建一个类来完成类似的事情:

class IntStringProcessor extends Processor<Integer, String> {}

和Class c = Class.forName("IntStringProcessor");

在运行时,您可以通过c.getGenericSuperclass() 获得实际的Processor 类型参数,但这扩展了我猜想的问题。

编辑:另一个想法

您的Processor 可能会保留类作为参数,然后:

Class c1 = Class.forName("java.lang.Integer");
Class c2 = Class.forName("java.lang.String");
Processor = new Processor(c1,c2);

您的处理器现在不是通用的,而是在内部使用 Class 实例。正如我所说,没有办法在运行时创建泛型实例,因为泛型只存在于编译时。

【讨论】:

  • 是的,但是我希望从配置文件中驱动 forName,所以我不想声明 IntStringProcessor :-)
  • 所以“错误的方式”就是答案。我猜你的设计中有一个错误导致你从配置文件创建类实例。
  • 不,这并不牵强。为什么我不能从配置文件动态创建新类型?
  • 我不确定你的意思。您想创建 java.lang.Class 的新实例。该类型必须已经存在于您的 jar 中。要添加新类型,您仍然需要重新编译您的锥体并重新部署,除非您使用 OSGi 或从 somwhere 加载类的自定义类加载器。我想你应该尝试使用组合而不是继承。
【解决方案2】:

首先让我整理一下。对于Processor&lt;Integer, String&gt;,Java 中没有Class 对象。 Processor 只有一个Class 对象,即Processor.class,所有泛型实例共享同一个Class 对象:

    Processor<Integer, String> x = new Processor<Integer, String>();
    Processor<Character, Byte> y = new Processor<Character, Byte>();
    System.out.println(x.getClass() == y.getClass());        // prints true

话虽如此,如果您只需要让泛型类型的令牌传递,您可以使用第三方实现。这里我使用Java ClassMate:

    TypeResolver typeResolver = new TypeResolver();
    // type token for Processor<Integer, String>
    ResolvedType type = typeResolver.resolve(Processor.class, Integer.class, String.class);
    System.out.println("type = " + type);

如果您从文件中读取 Java 类型的名称为 String,您可能希望使用 Class.forName() 而不是我的 .class 文字。

您必须自己解析 &lt;、&gt; 括号,但这应该很容易,因为这些单个字符在 Java 类型名称中总是表示相同的意思...

您可以像这样形成嵌套参数:

    // type token for Processor<Processor<Integer, String>, Character>
    ResolvedType type = typeResolver.resolve(Processor.class,
            typeResolver.resolve(Processor.class, Integer.class, String.class),
            typeResolver.resolve(Character.class)
    );
    System.out.println("type = " + type);

如果你想在编译时为你知道的类型创建类型标记,你可以使用所谓的super-type-token pattern。只需在new GenericType&lt;...&gt;() {} 中输入您需要的任何类型名称:

    type = typeResolver.resolve(new GenericType<Processor<Processor<Integer, String>, Character>>() {});
    System.out.println("type = " + type);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-03
    • 2011-04-02
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 2021-08-27
    相关资源
    最近更新 更多