首页 文章

类型擦除如何工作

提问于
浏览 576 次
5

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

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

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

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

  • 为什么匿名类包含泛型类型而不是类型擦除?

Updated: 4.在Kotlin中如何使用具体化的泛型以及为什么在Java中无法实现这么酷的东西?

Here非常明确地说明了仿制药的工作原理 . @Mibac

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

1 回答

  • 3

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

    在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级别上定义了albiet匿名 .

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

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

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

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

相关问题