为什么Java的类型擦除不会打破这个?

时间:2013-08-03 02:31:57

标签: java

public class Test {
    public static class Nested<T> {
        public T val;
        Nested(T val) { this.val = val; }
    }
    public static void main(String[] args) {
        Nested<Integer> a = new Nested<Integer>(5);
        Nested<Integer> b = new Nested<Integer>(2);
        Integer diff = a.val - b.val;
    }
}

上面的代码工作正常。但是,如果我向嵌套添加方法:

T diff(Nested<T> other) { return this.val - other.val; }

我收到编译错误:

operator - cannot be applied to T,T

这对我有意义。 T的类型在运行时被擦除,因此Java不能应用仅为某些类(如Integer)定义的运算符。但为什么a.val - b.val有效?

编辑:

很多好的答案。感谢大家。如果我理解正确的话,它的要点是编译器可以在a.val - b.val中向Integer添加强制转换,因为它知道ab被实例化为Nested<Integer>。但是,由于<Integer出现在泛型函数定义的主体内(其中T仍然可以是任何东西),编译器无法添加使“this.val - other.val”工作所必需的强制转换。这导致了一个更有趣的问题,即,如果Java编译器能够内联,那么像diff这样的泛型函数是否可以工作?

5 个答案:

答案 0 :(得分:9)

两者之间的区别在于你是在一个通用方法内还是在它之外。

您完全确定方法T内部不是Integer,因此无法应用运算符减去-。但是,当您在main()中时,在泛型方法之外,编译器知道您已使用Nested实例化Integer,因此它非常清楚如何应用运算符。尽管泛型的实现已经删除了为Nested<T>生成代码的类型,但编译器并没有考虑ab的{​​{1}}:它有足够的知识来插入适当的演员表,取消结果,并应用减号Nested<T>运算符。

答案 1 :(得分:8)

您收到编译时错误,而不是运行时错误。

public static void main(String[] args) {
    Nested<Integer> a = new Nested<Integer>(5);
    Nested<Integer> b = new Nested<Integer>(2);
    Integer diff = a.val - b.val;
}

这里,编译器知道两个T都是Integer。您刚刚声明了<Integer>

T diff(Nested<T> other) { return this.val - other.val; }

这里,编译器不确定T。它可能是任何东西。而且,只允许使用数字运算符-

答案 2 :(得分:1)

a.val - b.val有效,因为它由编译器验证,而不是在运行时验证。编译器“看到”您正在使用&lt; Integer&gt;编译并运行Ok,在运行时即使擦除也没有问题,因为编译器已经验证了。

答案 3 :(得分:1)

因为方法调用是在运行时并且在编译时检查a.val - b.val

  • 在第一种情况下,编译器知道类型为Integer- 整数允许操作。
  • 在第二种情况下,编译器事先不知道T的类型,因此不确定-操作是否有效。因此编译错误。

考虑我们将该方法用作diff(Nested<Book> other),因此无法从其他书籍中删除该书籍。

答案 4 :(得分:1)

因为代码不在嵌套中,所以类型是已知的。编译器可以清楚地看到a.val - b.val是一个Integer减去整数,可以自动装箱。编译器基本上将其重写为

Integer diff = Integer.valueOf(((Integer) a.val).intValue() - ((Integer) b.val).intValue())

.intValue和.valueOf调用来自自动装箱和自动拆箱。

对于编译器来说,类型强制转换是安全的,因为您使用了参数化类型Nested。

确实,从技术上讲,可以是其他东西,比如Calendar对象,因为类型在运行时是未知的。但是如果你使用泛型,编译器会相信你没有做任何愚蠢的事情来规避它。因此,如果a.val或b.val不是Integers,则会在运行时抛出ClassCastException。