【问题标题】:why does java allow this?为什么java允许这样做?
【发布时间】:2011-08-17 16:31:24
【问题描述】:

这样做在语法上是合法的:

String [] s = new String[1];
Object [] o = s;

o[0] = new Integer(42);

当然它会在运行时崩溃。

我的问题是:首先允许这项任务有什么意义?

【问题讨论】:

    标签: java syntax semantics


    【解决方案1】:

    问题是分配Object [] o = s; - 我想这就是你所说的“这个”的意思。

    技术术语是array covariance,如果没有它,您就无法编写通用处理数组的代码。例如,java.util.Arrays 中的大多数非原始数组方法将毫无用处,因为您只能将它们与实际的 Object[] 实例一起使用。显然,Java 的设计者认为这比完全的类型安全更重要。

    在查看 Java 5 中引入的 Java 泛型时,您会看到另一种解决方案:通过通配符显式协变。然而,这会导致相当大的复杂性(请参阅关于? 通配符的源源不断的问题),Java 的原始设计者希望避免复杂性。

    【讨论】:

      【解决方案2】:

      允许这种分配的意义在于禁止它会使以下事情变得不可能:

      ArrayList[] lists = new ArrayList[10];
      lists[0] = new ArrayList();
      
      List[] genericLists = lists;
      lists[0].add("someObject");
      

      如果编译器禁止您的 String -> Object 情况,那么它还必须禁止 ArrayList -> List 以及从子类分配给其超类类型之一的任何其他实例。这使得 Java 等面向对象语言的许多特性变得毫无用处。当然,更典型的做法是:

      List[] lists = new List[10];
      lists[0] = new ArrayList();
      lists[0].add("someObject");
      

      但无论如何,编译器无法过滤掉这些情况,同时不允许许多有用且合法的用例,因此程序员有责任确保他们所做的事情是理智的。如果你想要的是Object[],那么就这样声明你的变量。如果你声明某个东西为String[],将其转换为Object[],然后忘记你真正拥有的是String[],那么这只是程序员错误。

      【讨论】:

        【解决方案3】:

        这是不允许的,当你运行该代码时你会得到一个java.lang.ArrayStoreException: java.lang.Integer

        编译器允许这样做,因为您将 String[] 转换为 Object[],这是正确的。这类似于

        Integer i = new Integer(10);
        Object o = i;
        String s = (String) o;
        

        编译器不会抱怨,但您会在运行时收到 ClassCastExeption。

        【讨论】:

        • 但你根本没有回答这个问题。您只是在准确地重新陈述 OP 所说的内容。此外,OP 从未说过它是“允许”,OP 说它是 “句法上” 正确的,并且它在运行时崩溃。 OP的精心设计的问题非常清楚,您的答案没有回答任何问题......
        • 我只是通过显示与对象的类比来表明编译器不会生成错误或警告。更重要的是,当为数组赋值时,ArrayStoreException 等效于 ClassCastException,因为当类型不兼容时会抛出它们。但请随意不同意:)。
        【解决方案4】:

        编译器无法知道(没有静态分析)o 实际上是String[],因此即使在运行时失败也允许赋值。没有什么可以阻止例如Integer[] 被分配给o,它只是不会发生。

        【讨论】:

          【解决方案5】:

          必须允许赋值,因为编译器无法推断数组在运行时是否只保存(或不保存)字符串。它会抛出一个[ArrayStoreException][1],它会在运行时进行检查。

          考虑一下:

          String [] s = new String[1];
          Object [] o = s;
          
          o = new Integer[1];
          
          o[0] = new Integer(1);
          

          这种情况是有效的并且运行正常。为了提供更广泛的视角,恕我直言,数组是 Java 中的低级泄漏抽象。

          【讨论】:

            【解决方案6】:

            通常编译器无法判断o 是否已分配为String[]。考虑一下:

            String[] s = new String[1];
            Object[] o;
            
            if (complexFunction(System.currentTimeMillis())) {
              o = s;
            } else {
              o = new Integer[1];
            }
            
            o[0] = 42;
            

            编译器在设计时不会知道o 将采用什么类型 - 所以它只允许赋值。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-14
              • 2013-09-07
              • 1970-01-01
              • 2020-05-01
              • 2017-02-24
              • 1970-01-01
              • 2012-08-30
              • 1970-01-01
              相关资源
              最近更新 更多