什么是标量类型,为什么将未经检查的强制转换抑制为数组类型而不是标量类型风险更大?

时间:2013-02-20 18:00:49

标签: 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];
}

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

3 个答案:

答案 0 :(得分:5)

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

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

ruakh在评论中提到了另一个值得考虑的意见:“我会认为这与证据的复杂性无关,就像在发现错误时检测错误一样。我认为通常会有更少的错误错误但未经检查的强制转换为(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;
    }
}

答案 1 :(得分: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[]不是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--];
}

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

答案 2 :(得分:1)

在这种情况下,标量类型表示非数组类型。