Java原始类型和泛型的交互

7
如果我有一个堆栈类
class Stack<E> {}

现在,如果我执行以下操作:
1)Stack<Integer> s = new Stack() 2)Stack s = new Stack<Integer>() 3)Stack s = new Stack() 有人能解释一下这些交互(泛型<- >原始)会引起什么吗?
主要是关于第一点的疑问。事实上,如果我这样做,这个赋值就不安全了,因为这个栈可以存储除了Integer之外的其他类型。但是,如果我有一个push方法并尝试存储一个非整数值,编译器就会阻止我...那么我何时才会遇到这种不安全的操作呢?

我不明白 - 你想要一个可以推入除整数以外的其他东西(例如整数和双精度浮点数)的堆栈,还是只想使用一种类型(整数)的堆栈,并让编译器帮助你? - Roland Schneider
1
不,我想要一个只有一种类型的堆栈,并想知道如果我进行赋值会发生什么。 - xdevel2000
2个回答

7

这三种方式都是完全合法的,因为 StackStack<Integer> 在实际运行中没有区别,但这三种方式都会导致编译器警告。

Stack<Integer> s = new Stack()

这将导致“未经检查的转换”警告,因为将原始类型转换为参数化类型通常不安全。然而,在这种情况下这样做是完全安全的:推送Integer值不会导致任何错误;推送非Integer值将导致类型错误。
Stack s = new Stack<Integer>()

这是从参数化类型到原始类型的合法转换。您将能够推送任何类型的值。但是,任何此类操作都会导致“未经检查的调用”警告。

Stack s = new Stack()

再次强调,这是合法的,没有隐式转换。您将能够推送任何类型的值。但是,任何这样的操作都会导致“未经检查的调用”警告。

每当您引用类型Stack时,也可能会收到“原始类型”警告。


6

所有这些都不安全,因为Java泛型仅仅是一种语法糖,通过类型擦除实现。例如,下面的代码完全有效:

Stack<Integer> s = new Stack<Integer>();
Stack<Double> s2 = (Stack<Double>)s;
s2.push(3.3d);

Java泛型的作用是让你不必明确地进行对象转换。仅此而已。它们除了生成编译器和IDE警告外,没有任何其他作用。

它们仍然很有用,可以使您的代码更易读,减少错误,但从字节码级别来看,重要的是要理解它们并没有做任何实质性的事情。


OP的情况#2不是编译器足够聪明以推断s的通用类型参数的情况吗? - Jason S
是的,但在第一点中,如果我尝试放置(使用push操作)除Integer以外的对象,编译器会提醒我。因此,泛型可能不仅是“语法糖”,而且第一点还改变了编译器的行为(语义)。所以我不明白哪里不安全了...请给我更详细的解释好吗? - xdevel2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接