关于Java中的通用数组创建

时间:2016-01-18 22:40:47

标签: java generics

刚刚发现了一行我不太了解的代码。

List<String>[] stringLists = new List[1];

此行可以在我的AndroidStudio IDE上传递编辑,但会发出警告。它似乎违反了面向对象语言的基本规则:“超类对象可以用子类实例实例化,但反之则不然。”

String[] ss = new Object[1];  // won't compile

我们知道List是任何通用List类型的超类型,例如本例中的List<String>。并且由于数组是协变的,所以我认为List []类型是List<String>[]类型的超类。为什么List<String>[]可以使用List[]进行实例化?

4 个答案:

答案 0 :(得分:5)

我认为JLS, §4.8, "Raw Types"回答了您为什么该作业有效的实际问题:

  

原始类型的使用仅允许作为遗留代码兼容性的让步。在将泛型引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。未来版本的Java编程语言可能会禁止使用原始类型。

这是为了与pre-generics代码兼容。一般来说,如果你想要类型安全,你根本不应该使用参数化类型的数组,如果作者写道,那会更好:

List<List<String>> lists = new ArrayList<>();

您对违反安全类型规则的假设是错误的。您说List不是List<String>的子类型。但是,在Java类型系统中,问题的答案是:“ListList<String>的子类型吗?”既不是“是”,也不是“不”。这是“这个问题无法回答。”

(混淆“类型”和“类”也可能不正确。只有一个List类,但List<String>List<Object>是不同的类型。 :List<String> List<Object>的子类型。)

要添加更多有关正在发生的事情的详细信息,JLS会解释§5.2, "Assignment Contexts"中变量分配中允许的转换次数。值得注意的是,该列表以:

结尾
  

如果在应用上面列出的转化后,结果类型为原始类型(§4.8),则可以应用未经检查的转化(§5.1.9)。

后一个链接§5.1.9, "Unchecked Conversions",在一些形式主义解释未经检查的转换是什么之后,重申理由(强调我的):

  

未经检查的转换用于实现遗留代码的平滑互操作(在引入泛型类型之前编写)与已经过转换以使用泛型的库(我们称之为泛化的过程)。在这种情况下(最值得注意的是java.util中的集合框架的客户),遗留代码使用原始类型(例如Collection而不是Collection<String>)。原始类型的表达式作为参数传递给库方法,这些库方法使用相同类型的参数化版本作为其相应形式参数的类型。

     

在使用泛型的类型系统下,此类调用无法显示为静态安全。拒绝此类调用会使大量现有代码无效,并阻止它们使用较新版本的库。反过来,这将阻止图书馆供应商利用通用性。为了防止这种不受欢迎的事件转换,可以将原始类型转换为原始类型引用的泛型类型声明的任意调用。 虽然转换不健全,但可以容忍对实用性的让步。在这种情况下会发出未经检查的警告。

官方的故事确实是从原始类型到参数化类型的未经检查的转换被故意添加到语言中,尽管可能不安全,并且出于兼容性原因仅标记了编译警告。 (Java非常努力确保在版本X中编译的代码永远不会在版本X + 1中编译或停止使用。)

答案 1 :(得分:2)

Java中的泛型只是编译时的语法糖。在执行时,两个列表都是List<Object>

答案 2 :(得分:2)

在Eclipse中编译我得

Type safety: The expression of type List[] needs unchecked conversion to conform to List<String>[]

这是正确的警告。尝试

List<String>[] sl = new ArrayList<String>[1];

我得到了

Cannot create a generic array of ArrayList<String>

在您的代码中,您 能够实例化List非通用数组,并使用未经检查的转换将其分配给List<String>[]

答案 3 :(得分:0)

  

这行可以通过我的AndroidStudio IDE传递编译   警告。它似乎违反了面向对象的基本规则   language:“超类对象可以用子类实例化   例如,但反之亦然。“

该行:

void go()
{
    int ints[5];
    cout<<"here";
    fill(ints,ints+sizeof(ints),0);
    cout<<"here2";
}

在我看来,上面不应该编译。承认Java中的数组是协变的,但我认为得出List<String>[] stringLists = new List[1]; 是其原始类型的超类是不合理的。

我能够在JLS中找到这个,第4.3节:

  

Java编程语言中有一些泛型   使用类或接口名称时不提供类型参数。这样   上下文不涉及原始类型的使用(§4.8)。相反,他们是   类型参数不需要或与之无关的上下文   泛型类或接口的含义。

也许,出于上述赋值的目的,允许原始类型,因为“类型参数对于......泛型类或接口的含义是不必要的”。将原始类型分配给List<String>后,如果为真,则必须为stringLists不可接受的意外。