类型擦除如何工作

hlu*_*kyi 5 java generics jvm kotlin

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

interface MyService {

    @GET("path")
    Call<MyData> getData();
}
Run Code Online (Sandbox Code Playgroud)

我很困惑 - 如何才能从这个界面获得正确的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();
}
Run Code Online (Sandbox Code Playgroud)

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

System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass());
Run Code Online (Sandbox Code Playgroud)

AbstractList<E>这段代码打印原始

System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass());
Run Code Online (Sandbox Code Playgroud)

打印ArrayList<Integer>.

这并不是让我困惑的最后一件事.在Kotlin中有一些具体的泛型,在编译时看起来像是一些黑客,但我们可以轻松地从泛型中获取类:

inline fun <reified T> test() {
    print(T::class)
}
Run Code Online (Sandbox Code Playgroud)

而现在我完全混淆了类型擦除机制.

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

更新: 4.如何在Kotlin中使用具体化的泛型以及为什么这样的酷事不能用Java实现?

这里解释了如何使用仿制药.@Mibac

您只能将reified与内联函数结合使用.这样的函数使得编译器将函数的字节码复制到每个使用函数的地方(函数被"内联").当您使用reified类型调用内联函数时,编译器会知道用作类型参数的实际类型,并修改生成的字节码以直接使用相应的类.因此,像myVar这样的调用是T变为myVar是String,如果类型参数是String,则在字节码和运行时.

Eug*_*Loy 3

\n

有人可以解释一下,为什么有时它包含信息,有时不包含信息?

\n
\n\n

在 jvm 级别有一个Signature 属性

\n\n
\n

记录在 Java 编程语言中声明使用类型变量或参数化类型的类、接口、构造函数、方法或字段的签名 (\xc2\xa74.7.9.1)。

\n
\n\n

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

\n\n
\n

为什么匿名类保留泛型类型并且不进行类型擦除?

\n
\n\n

你打电话时:

\n\n
System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass());\n
Run Code Online (Sandbox Code Playgroud)\n\n

...没有任何内容是根据jvm 类来定义的,而:

\n\n
System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass());\n
Run Code Online (Sandbox Code Playgroud)\n\n

...实际上在jvm 级别上定义了类,尽管在java 语言级别上是匿名的。

\n\n

这就是为什么反射对于这些情况给出不同结果的原因。

\n\n
\n

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

\n
\n\n

\n\n

\n

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

\n
\n\n

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

\n