繁体   English   中英

类型擦除如何工作

[英]How type erasure works

我正在调查创建代理对象的库是如何工作的,特别是我想了解它们如何从声明的方法中获取类型。 例如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<E>

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

打印ArrayList<Integer>

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

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

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

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

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

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

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

有人可以解释一下,为什么有时它会保存信息而有时却没有?

在jvm级别上有一个Signature属性

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

您可能希望拥有它的原因之一是,例如,编译器需要知道来自预编译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级别上定义了albiet匿名。

这就是为什么反射为这些情况提供不同结果的原因。

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

如何在Kotlin中使用具体化的泛型,以及为什么这些很酷的东西不能在Java中实现?

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM