java – 为什么这个涉及通用接口方法的程序编译?

以下代码在运行时抛出ClassCastException,并且行public String foo(){return“bar”;生成警告“找到’ java.lang.String’,需要’T’”.我理解ClassCastException(调用接口方法,T等于Integer但foo返回一个String),我理解警告(它试图警告我们这个问题).但我不明白的是程序编译的原因.为什么返回String的方法允许覆盖返回T的方法?

public class Main {

    interface MyInterface {

        <T> T foo();
    }

    static class MyClass implements MyInterface {

        @Override
        public String foo() { return "bar"; }
    }

    public static void main(String[] args) {
        int a = ((MyInterface) new MyClass()).<Integer>foo();
    }
}
这是一个非常深刻的问题. Java语言规范 writes

在类C中声明或继承的实例方法mC,覆盖C类中声明的另一个方法mA,iff以下所有条件都为真:

> mC的签名是mA签名的子签名(§8.4.2).
> ……

and

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.

在我们的例子中显然不是这样,因为MyInterface.foo声明了一个类型参数,但是MyClass.foo没有.

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or
  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

该规范解释了第二个条件的必要性如下:

The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.

事实上,在我们的情况下满足第二个条件,因为MyClass.foo具有签名foo(),这也是MyInterface.foo签名的擦除.

这留下了不同回报类型的问题.规范writes

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.

and

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2 iff any of the following is true:

  • If R1 is a reference type then one of the following is true:

    • R1, adapted to the type parameters of d2 (§8.4.4), is a subtype of R2.

    • R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).

    • d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.

在我们的例子中,R1 = String和R2 = T.因此,第一个条件是false,因为String不是T的子类型.但是,String可以通过未经检查的转换转换为T,使第二个条件成立.

该规范解释了第二和第三个条件的必要性如下:

An unchecked conversion is allowed in the definition, despite being unsound, as a special allowance to allow smooth migration from non-generic to generic code. If an unchecked conversion is used to determine that R1 is return-type-substitutable for R2, then R1 is necessarily not a subtype of R2 and the rules for overriding (§8.4.8.3, §9.4.1) will require a compile-time unchecked warning.

也就是说,编译器接受了您的代码,因为您无意中使用了两个引入的编译器功能,以便通过允许逐步生成现有代码来简化向泛型的转换.这些功能在编译时类型系统中打开了一个循环漏洞,这可能会导致堆污染和奇怪的ClassCastExceptions行甚至可能在源代码中没有强制转换.为了提醒您这种危险,编译器需要发出未经检查的警告.因此,这些特征仅应用于其预期目的(与非通用遗留代码的兼容性),否则应避免使用.

相关文章
相关标签/搜索