【问题标题】:How type erasure works类型擦除的工作原理
【发布时间】:2017-09-27 16:37:38
【问题描述】:

我正在研究创建代理对象的库是如何工作的,特别是我想了解它们如何从声明的方法中获取类型。例如适用于 Android 的流行库 - Retrofit:

interface MyService {

    @GET("path")
    Call<MyData> getData();
}

我很困惑 - 怎么可能从这个接口得到完全 MyData 类而不是原始对象?因为根据我的理解,类型擦除将删除放置在通用大括号内的任何信息。

我写了一些测试代码,令我惊讶的是,从这些代码中获取类型真的很容易:

@org.junit.Test
public void run() {
    Method[] methods = Test.class.getDeclaredMethods();
    Method testMethod = methods[0];
    System.out.println(testMethod.getReturnType());
    ParameterizedType genericType = ((ParameterizedType) testMethod.getGenericReturnType());
    Class<Integer> clazz = (Class<Integer>) genericType.getActualTypeArguments()[0];
    System.out.println(clazz);
}

interface Test {
    List<Integer> test();
}

它看起来有点脏,但它可以工作并打印Integer。这意味着我们在运行时有类型。此外,我还阅读了有关匿名类的另一个肮脏技巧:

System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass());

在这段代码时打印原始AbstractList&lt;E&gt;

System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass());

打印ArrayList&lt;Integer&gt;

让我感到困惑的并不是最后一件事。在 Kotlin 中,有一些具体化的泛型,它们在编译时看起来像是一些 hack,但我们可以很容易地从泛型中获取类:

inline fun <reified T> test() {
    print(T::class)
}

现在我完全对类型擦除机制感到困惑。

  1. 谁能解释一下,为什么有时它包含信息而有时却没有?
  2. 为什么泛型没有在 Java 中以正常方式实现?是的,我读到它可能会破坏与以前版本的兼容性,但我想了解如何。为什么泛型返回类型除了new ArrayList&lt;Integer&gt;does 不会破坏任何东西?
  3. 为什么匿名类拥有泛型类型而不是类型擦除?

更新: 4. reified generics 在 Kotlin 中是如何工作的,为什么这么酷的东西不能在 Java 中实现?

Here 非常清楚地解释了具体化泛型是如何工作的。 @Mibac

您只能将 reified 与内联函数结合使用。这样的 函数使编译器将函数的字节码复制到每个 使用函数的地方(函数被 “内联”)。当您调用具有具体类型的内联函数时, 编译器知道用作类型参数的实际类型并修改 生成的字节码直接使用对应的类。 因此,像 myVar is T 这样的调用变成 myVar is String,如果类型 参数是字符串,在字节码和运行时。

【问题讨论】:

标签: java generics jvm kotlin


【解决方案1】:

谁能解释一下,为什么有时它包含信息而有时却没有?

在 jvm 级别有一个Signature attribute

为其在 Java 编程语言中的声明使用类型变量或参数化类型的类、接口、构造函数、方法或字段记录签名(第 4.7.9.1 节)。

您可能想要拥有它的原因之一是,例如,编译器需要知道来自预编译的some.class(来自第三方some.jar)的some_method 的实际参数类型.在这种情况下,假设方法采用 Object 可能会违反编译 some.class 的假设,并且您不能以类型安全的方式调用 some_method

为什么匿名类持有泛型类型而不是类型擦除?

当你打电话时:

System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass());

...jvm 类没有任何定义,而这个:

System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass());

...实际上在jvm 级别 定义了类,在java laguage 级别 上虽然是匿名的。

这就是反射在这些情况下给出不同结果的原因。

为什么泛型没有在 Java 中以正常方式实现?是的,我读到它可能会破坏与以前版本的兼容性,但我想了解如何。为什么泛型返回类型除了新的 ArrayList 不会破坏任何东西?

具体化的泛型在 Kotlin 中是如何工作的,为什么这么酷的东西不能在 Java 中实现?

我认为你不能对这些给出不基于意见的客观答案。此外,我建议拆分或缩小您的问题范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多