【问题标题】:Redundant wording in a Java array and ArrayList declarationJava 数组和 ArrayList 声明中的冗余措辞
【发布时间】:2015-12-19 22:43:43
【问题描述】:

Java 中典型的ArrayList 声明属于以下类型:

ArrayList<Object> name = new ArrayList<Object>();

同样,对于一个数组,我们有:

Object[] name = new Object[size];

现在,我们肯定不能有以下类型的声明:

ArrayList<Object> name = new ArrayList<AnotherObject>();

两次指定同一对象的类型和名称对我来说似乎是多余的。上述声明中的冗余可以通过以下方式轻松消除:

ArrayList<Object> name = new();

是否有任何具体的原因让我错过了为什么按照它的方式完成?

【问题讨论】:

    标签: java arrays arraylist


    【解决方案1】:

    不是。至少,不再是了。现在你可以这样做了:

    ArrayList<Object> name = new ArrayList<>();
    

    您不能省略第二个ArrayList,因为它不一定要与第一个匹配。例如,您可以这样做:

    List<Object> name = new ArrayList<>();
    

    您会得到一个ArrayList,但将其视为List

    【讨论】:

    • 您也可以选择我认为的泛型参数的子类型,所以 OP 是否错了? ArrayList&lt;Object&gt; name = new ArrayList&lt;SomeSubTypeOfObject&gt;();
    • 令人惊讶的是,ArrayList&lt;SomeSubTypeOfObject&gt; 不是ArrayList&lt;Object&gt; 的子类型。但是,只要您不想在其中插入任何内容,这将起作用:`ArrayList name = new ArrayList();'
    【解决方案2】:

    从 JDK7 开始,存在类型推断。您不需要在右侧指定类型。看这里的例子:http://www.javacodegeeks.com/2012/05/type-inference-from-java-7.html

    你可以这样写:

    List<Object> values = new ArrayList<>();
    

    【讨论】:

      【解决方案3】:

      关于它为什么会以这种方式完成,有一本尘土飞扬的大部头,但让我们具体看看 ArrayList 的声明。

      要说明这一点:在 Java 6 发布时,该语言还没有在新泛型时减少冗长的能力。

      This is the grammar for declaring a new instance of a reference, back in Java 6.

      ClassInstanceCreationExpression:
         new TypeArguments (opt) ClassOrInterfaceType ( ArgumentList (opt) )
      ClassBody (opt)
              Primary. new TypeArguments (opt) Identifier TypeArguments (opt) (
      ArgumentList (opt) ) ClassBody (opt)
      
      ArgumentList:
              Expression
              ArgumentList , Expression
      

      第二个产生式,或者更具体地说,TypeArguments 标记是控制我们可以放在那里的东西。

      That changes in Java 7,引入菱形算子。

      ClassInstanceCreationExpression:
          new TypeArguments (opt) TypeDeclSpecifier TypeArgumentsOrDiamond (opt)
                                                                  ( ArgumentList (opt) ) ClassBody (opt)
          Primary . new TypeArguments (opt) Identifier TypeArgumentsOrDiamond (opt)
                                                                  ( ArgumentList (opt) ) ClassBody (opt)
      
      TypeArgumentsOrDiamond:
          TypeArguments
          <>
      
      ArgumentList:
          Expression
          ArgumentList , Expression
      

      因此,由于 TypeArgumentsOrDiamond 令牌,冗长不再需要存在,但无论如何,较新版本的 Java 仍然支持它。

      以你的例子为例,为什么我们不能这样做:

      ArrayList<Object> name = new();
      

      想到的主要事情是,如果你想创建一个List 的实例,它是一个接口,因此不能被实例化,new() 方法如何知道是哪一种 的列表拉回来?它总是会撤回ArrayList吗? LinkedList?还是您自己的列表类型之一?没有你告诉 Java 怎么知道哪个是正确的?

      更直接地说,这个应该产生什么......

      List<Object> name = new();
      

      ...如果我们可以在 ArrayListLinkedList 或扩展 ArrayListSpecialUtilityList 之间进行选择?

      【讨论】:

        【解决方案4】:

        也许您熟悉其他能够推断变量类型的语言,例如 C# 或 C++;例如

        var x = new MyObjectType();  // C#
        

        auto x = someMethodThatReturnsSomething();  // C++
        

        如果您习惯于上述任何一种语言,Java 会“错误地”进行类型推断:您可以在右侧省略泛型类型的类型参数:

        List<MyObjectType> = new ArrayList<>();
        

        然而,尽管这起初看起来不直观,但这就是类型推断的工作原理,它是一致的,并且在其他情况下具有优势;例如,您可以将 Collections.emptyMap() 传递给采用 Map&lt;Integer, MyObjectType&gt; 的方法,编译器会自动解决,而无需指定您希望 emptyMap 返回的映射类型。

        至于为什么你仍然必须在 LHS 上指定类型,这是 Java 哲学 - 你在 LHS 上指定你想要的interface,在 LHS 上指定你想要的具体类型 RHS。

        【讨论】:

          【解决方案5】:

          从 Java 10 开始,那些多余的信息可以省略,我们可以选择让编译器使用 var 来推断类型,例如

          var objArray = new Object[10]; // (objArray instanceof Object[]) == true
          var stringList = new ArrayList<String>(); // (stringList intanceof ArrayList) == true
          

          为了处理var,编译器查看所谓的initializer(声明的右侧),并将其类型用于变量。详情可参考JEP 286

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-24
            • 2011-07-01
            • 1970-01-01
            • 2013-02-15
            • 2011-10-20
            • 2013-01-20
            相关资源
            最近更新 更多