当前位置: 首页 > 知识库问答 >
问题:

在这种情况下,为什么泛型类型的擦除会阻止重写?

公孙弘图
2023-03-14

下面的代码没有在OpenjDK 11上编译。在我看来,B中的test1应该覆盖A中的test1,因为:

  1. 方法有相同的名称。
  2. 这些方法具有相同的参数列表。
  3. 这些方法具有相同的可见性。
  4. 这些方法实际上不会抛出可能不兼容的检查异常。
  5. 它们的返回类型是协变的。

我使用了一个反编译器,分别对每个类进行反编译。编译后的代码实际上像我期望的那样工作。它用数字代替U扩展数字,用整数代替T扩展整数,以此类推。但当我试图将这两个类一起编译时,第二个类出现了一个错误,即第二个类中的测试没有覆盖第一个类中的方法。

我错过了一些大大小小的东西。它可能与5有关。也许类型不是协变的。你能帮帮我吗?

class A {
    <U extends Number, T extends Number> U test(T test) {
        System.out.println("In A.test(T test)");
        return null;
    }
    //Decompiler shows that the above method erases to 
    // Number test(Number test)
    // Just like the method below.
        
    Number test2(Number test) {
        return null;
    }
}

class B extends A {
    //Unsuccessful override. Compiler error.
    @Override  
    <U extends Integer, T extends Number> U test(T test) {
        System.out.println("In B.test(T tesT)");
        return null;
    }
    //Decompiler shows that the above method erases to 
    //Integer test(Number test)
    //Just like the method bellow.
    
    
    //Successful override
    @Override
    Integer test2(Number test) {
        return null;
    }
}

共有1个答案

相野
2023-03-14

test方法产生错误的原因是它们具有不同的签名。请注意,方法的签名由其名称、参数列表和类型参数组成。

引用Java语言规范:

两个方法或构造函数,M和N,如果具有相同的名称、相同的类型参数(如果有的话),则具有相同的签名(§8.4.4),并且,在将N的形式参数类型适应M的类型参数之后,具有相同的形式参数类型。

至关重要的是,您的两个test方法没有相同的类型参数,因为A. test中的UB. test中的U有不同的绑定。

两个方法或构造函数M和N具有相同的类型参数,如果以下两个都是true:

>

  • M和N具有相同数量的类型参数(可能为零)。

    其中A1。。。,An是M和B1的类型参数。。。,Bn是N的类型参数,设θ=[B1:=A1,…,Bn:=An]。那么,尽管我(1≤ 我≤ n) ,Ai的界与应用于Bi的界的θ的类型相同。

    想想如果B. test实际上覆盖了A. test会发生什么。您可以将类型传递给超出其边界的类型参数U

    A a = new B();
    // This will call B.test, and U is Double, T is Integer
    // but U should extends Integer!
    Double x = a.test((Integer)0); 
    

  •  类似资料:
    • 无法编译以下内容: 特别是,使用javac编译时,错误将是: 但下面的汇编很好: 唯一的区别是我引入了一个额外的变量。请注意,我没有施法,所以没有语义变化。 有人能解释为什么需要这样做吗?

    • 问题内容: 我偶然发现该语句(从一些更复杂的代码中摘录)进行编译: 在短暂但快乐的时刻,我认为受检查的异常最终决定已经死亡,但是对此仍然感到遗憾: 该块不必为空;似乎可以有代码,只要该代码不引发检查异常即可。这似乎是合理的,但是我的问题是,语言规范中的哪个规则描述了此行为?据我所知,§14.18throw语句明确禁止使用它,因为表达式的类型是已检查的异常,并且不会被捕获或声明为被抛出。(?) 问题

    • 根据Java教程 将包装类型(整数)的对象转换为其相应的基元(int)值称为取消装箱。当包装类的对象为: 作为参数传递给需要相应基元类型的值的方法 分配给相应基元类型的变量 为什么在这种情况下会发生拆箱? 在这种情况下,这些事情发生在哪里?是否有管理数组中元素访问的底层方法?或者[]暗示某种变量?

    • 问题内容: 我编写了以下代码来实现Singleton模式: 当我编译此文件时,它应该生成Test.class和Test $ TestHolder.class,但它还会生成Test $ 1.class。这没有道理。那么,为什么以及如何呢? 问题答案: 类需要在中调用私有构造函数。但是它是私有的,实际上不能从另一个类中调用。因此,编译器发挥了作用。它 添加了一个仅知道的新的非私有构造函数!_该构造函数

    • 问题内容: 我以为Java擦除会在编译时消除泛型类型,但是当我自己对其进行测试时,我意识到在Bytecode中有一些有关泛型类型的信息。 这是我的测试: 我写了2节课: 和 我编译了两个类,并在通用类的某个地方看到了这一行 在非泛型类中: 所以很明显我在字节码中有通用信息,那么这个擦除的东西是什么? 问题答案: 一些通用类型信息存储在属性中。请参阅JLS 4.8 和4.6以及JVM规范4.3.4。

    • 我有一个返回泛型的函数: 所以,当我试图匹配一个函数结果时,我的问题是: 我得到一个警告:“类型模式数组[CustomerInfo到[CustomerApplication到]]中的非变量类型参数CustomerApplication-DDTO未选中,因为它已被擦除消除。” 这是否意味着在Array[]中可以得到任何类型的数组?所以我已经阅读了关于ClassTag和TypeTag的文章,但是误解了