Gee*_*eek 4 java generics effective-java
来自Effective Java Item 26 偏好通用类型
在所有其他条件相同的情况下,将未经检查的强制转换抑制为数组类型而不是标量类型风险更大,这表明第二种解决方案.但是在一个比Stack更实际的泛型类中,你可能会在代码中的许多点读取数组,因此选择第二个解决方案需要多次转换为E而不是单个转换为E [],这就是为什么第一种溶液更常用[Naftalin07,6.7].
作者scalar type在这里是什么意思,他想在这里传达什么?什么是选项1被认为比选项2更危险?
代码 :
// The elements array will contain only E instances from push(E).
// This is sufficient to ensure type safety, but the runtime
// type of the array won't be E[]; it will always be Object[]!
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
Run Code Online (Sandbox Code Playgroud)
VS
// Appropriate suppression of unchecked warning
public E pop() {
if (size == 0)
throw new EmptyStackException();
// push requires elements to be of type E, so cast is correct
@SuppressWarnings("unchecked") E result =
(E) elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
Run Code Online (Sandbox Code Playgroud)
此示例中的标量类型是单个值,而不是由多个值组成的数组,如数学向量.E []是数组,E是标量.
我最初的想法是,Joshua Bloch认为在数组的情况下抑制未经检查的强制转换警告风险更大,因为证明代码的类型安全性不会发生任何错误会更加复杂.
另一个值得考虑的意见是由ruakh在评论中提到的:"我认为并不是说证据的复杂性,而是在发现错误时检测错误.我认为错误之间通常会有较少的"距离". -but-unchecked强制转换为(E)和后续隐式强制转换引发ClassCastException,而不是使用转换为(E [])而不是"
第三种观点(如果我理解正确的话,这是无可争辩的想在他的答案中指出的,无论如何这是我的新观点)是阵列演员是"冒险的"因为这个阵列不能在这之外使用类.(E[])是一个未经检查的强制转换:由于类型擦除,运行时无法真正检查此(不正确)强制转换的正确性.我们摒弃了一个肮脏的技巧,但是如果某个方法将此数组作为E []返回,并且它将被分配给客户端类中的E [],那么它在运行时仍然会因ClassCastException而失败:
public class Test {
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
String[] array = stack.getArray(); // ClassCastException at runtime here!
}
}
class Stack<E> {
E[] elements;
public Stack() {
elements = (E[]) new Object[10];
}
// oh no, our dirty-tricky array escapes!
E[] getArray() {
return elements;
}
}
Run Code Online (Sandbox Code Playgroud)
理想情况下我们想写
E[] elements;
public Stack()
{
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,Java犯了一个巨大的错误并且不允许这样做.所以我们需要解决方法.
这种解决方法
E[] elements;
public Stack()
{
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
Run Code Online (Sandbox Code Playgroud)
在Java类型系统中理论上是错误的,因为Object[]它不是一个子类型E[].它碰巧在运行时在今天的 JVM上工作,但是,我们不应指望它永远工作.嗯,实际上,人们确实指望这一点,我认为它不会有任何改变的机会.所以没人关心.
(更正:实际上语言规范§5.5特别允许强制转换在运行时工作,因此每个规范的代码都没有错.然而,它太过于hackish,它不是"普通"类型系统的一部分,它的正确性是基于一些我们不想真正学习的妥协.)
从实际和理论上讲,第二种解决方法是正确的
Object[] elements;
public Stack()
{
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public push(E e)
{
...
elements[size++] = e;
}
public E pop()
{
...
E result = (E)element[size--];
}
Run Code Online (Sandbox Code Playgroud)
从Object到E的转换是正确的,因为程序逻辑确保它必须是一个E.