4

来自 Effective Java Item 26 Favor Generic types

在所有其他条件相同的情况下,将未经检查的强制转换抑制为数组类型比标量类型风险更大,这将建议第二种解决方案。但是在比 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];
}

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;
}                    
4

3 回答 3

5

此示例中的标量类型是单个值,而不是由多个值组成的数组,例如数学向量。E[] 是数组,E 是标量。

我最初的想法是 Joshua Bloch 认为在数组的情况下抑制未经检查的强制转换警告风险更大,因为要证明代码的类型安全不会发生任何错误会更加复杂。

ruakh 在评论中提到了另一个值得考虑的观点:“我认为这与证明的复杂性无关,而是在存在错误时检测错误。我认为错误之间的“距离”通常会更小-but-unchecked cast to (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;
    }
}
于 2013-02-20T18:07:50.920 回答
5

理想情况下,我们想写

E[] elements;

public Stack() 
{
    elements = new E[DEFAULT_INITIAL_CAPACITY];
}

不幸的是,Java 犯了一个巨大的错误并且不允许这样做。所以我们需要变通方法。

这种解决方法

E[] elements;

public Stack() 
{
    elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}

在 Java 类型系统中理论上是错误Object[]的,因为 an不是E[]. 它恰好在今天的JVM上运行时工作,但是,我们不应该指望它永远工作。嗯,实际上,人们确实指望这一点,而且我看不出它有任何改变的机会。所以没人在乎。

(更正:实际上语言规范 §5.5 明确允许强制转换在运行时工作,因此每个规范的代码都没有错。但是,它太老套了,它不是“正常”类型系统的一部分,它的正确性基于一些我们并不想学习的妥协。)

第二种解决方法在实践和理论上都是正确的

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--];
}

从 Object 到 E 的转换是正确的,因为程序逻辑确保它必须是E.

于 2013-02-20T18:57:39.440 回答
1

在这种情况下,标量类型意味着非数组类型。

于 2013-02-20T18:04:29.457 回答