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添加强制转换,因为它知道a
和b
被实例化为Nested
。但是,由于<Integer
><Integer
出现在泛型函数定义的主体内(其中T仍然可以是任何东西),编译器无法添加使“this.val - other.val
”工作所必需的强制转换。这导致了一个更有趣的问题,即,如果Java编译器能够内联,那么像diff这样的泛型函数是否可以工作?
答案 0 :(得分:9)
两者之间的区别在于你是在一个通用方法内还是在它之外。
您完全确定方法T
内部不是Integer
,因此无法应用运算符减去-
。但是,当您在main()
中时,在泛型方法之外,编译器知道您已使用Nested
实例化Integer
,因此它非常清楚如何应用运算符。尽管泛型的实现已经删除了为Nested<T>
生成代码的类型,但编译器并没有考虑a
和b
的{{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。