【问题标题】:java generics runtime typejava泛型运行时类型
【发布时间】:2014-02-11 01:30:03
【问题描述】:

post I found by Ian Robertson 有一个很好的实用方法来确定 Java 类类型参数的运行时类。它比快速版本更进一步:

ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
Class<?> c = (Class<?>) pt.getActualTypeArguments()[0]

对于从抽象参数化父 (class Foo&lt;T&gt; extends Bar&lt;T&gt;) 或参数化抽象类 (new Foo&lt;Bar&gt;() {}) 的匿名实例继承的参数化子代,这两种方法都像一个魅力,并使用具体类型进行实例化。


我失败的地方是尝试对通过类型参数实例化的其他对象做同样的事情。相关的类对象是:

public class Foo {/* code omitted for brevity */}

public class MyFoo extends Foo {/* code omitted for brevity */}

public abstract class AbstractLoader<T extends Foo> {/* code omitted for brevity */}

public abstract class AbstractReader<T extends Foo> {/* code omitted for brevity */}

public class MyReader<T extends Foo> extends AbstractReader<T> {/* code omitted for brevity */}

public class Loader<T extends Foo> extends AbstractLoader<T> {
    /* code omitted for brevity */

    public MyReader<T> getReader() {
        // Parameterized type "T" doesn't seem to carry through here
        return new MyReader<T>();
    }
}

示例代码:

static void main(String... s) {
    Loader<MyFoo> loader = new Loader<>(); // new Loader<MyFoo>() for Java < 1.7
    MyReader<MyFoo> = loader.getReader();

    ParameterizedType pt = (ParameterizedType)loader.getClass().getGenericSuperclass();
    System.out.println("LoaderType = " + pt.getActualTypeArguments()[0]);
    // Prints: LoaderType = MyFoo

    pt = (ParameterizedType)reader.getClass().getGenericSuperclass();
    System.out.println("ReaderType = " + pt.getActualTypeArguments()[0]);
    // Prints: ReaderType = T
}

直觉告诉我这个“应该”以某种方式是可能的,但我似乎无法找到正确的答案。 OTOH,这可能是由于类型擦除而“无法做到”的另一个例子。

【问题讨论】:

  • 请发帖SSCCE。您现有的示例不完整并且存在编译错误。特别是,loader 的泛型类型不应该是可具体化的 - 你需要做 new Loader&lt;MyFoo&gt;() { }
  • Paul,我已经更新了示例代码;对于那个很抱歉。我确实尝试了 new Loader&lt;&gt;()new Loader&lt;MyFoo() 并得到了相同的结果。
  • 您的示例仍然是错误的 - 它打印“LoaderType = T ReaderType = T”(在修复 reader 声明之后)。请确保您的问题的前提是有道理的,或者删除它。

标签: java generics reflection runtime


【解决方案1】:

你完全误解了一些东西。 new Loader();new Loader&lt;Integer&gt;();new Loader&lt;Object&gt;(); 等在运行时没有区别。不可能把它们区分开来。立即摆脱这种想法。

如果我们创建一个类,那么关于该类的声明中的类型(包括泛型),包括超类、方法类型、字段类型等都存储在类文件中。可以在运行时检索此信息。

因此,当您拥有new Foo&lt;Bar&gt;() {} 时,它会创建某个类(匿名类)的实例,该实例使用特定类型参数扩展泛型类型。类似于:

class SomeAnonymousClass extends Foo<Bar> {
}
new SomeAnonymousClass()

Foo&lt;Bar&gt; 在编译时被硬编码为该类的超类,这可以在运行时检索。

但是您的代码没有做任何此类事情。你没有创建任何Loader 的子类。

【讨论】:

    【解决方案2】:

    您找不到对象的类型参数 - 它在运行时被删除。

    如果可以的话,Loader loader0Loader&lt;MyFoo&gt; loader1Loader&lt;MyBar&gt; loader2 会给出不同的结果。这种差异必须在运行时以某种方式表示:直接在对象中,或者通过引用不同的类。第一个变体需要为每个实例额外的内存,被认为是不合适的。第二个需要在运行时创建类,因为类加载器本身不能包含参数化类加载器的所有可能变体——它们在编译时是未知的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      相关资源
      最近更新 更多