scala类型系统:20) 数组类型

在java里,引用类型的数组类型是支持协变的,即 String[] 类型是 Object[] 的子类型,martin问过gosling,回答是希望有一个通用处理数组的简单方法。

void sort(Object[] a, Comparator cmp) { … }

数组的协变被用来确保任意参数类型的数组都可以传入排序方法。随着java引入了泛型,sort方法可以用类型参数,因此数组的协变不再有用。只是考虑到兼容性。

scala里不支持数组的协变,以尝试保持比java更高的纯粹性。即 Array[String]的实例对象不能赋给Array[Any]类型的变量。

在数组的设计上,scala是走过弯路的,这里有篇文章翻自martin写的关于scala2.8数组的paper,已经解释的非常好了。

正是因为2.8中的数组背后的实现要与java保持一致,使得通过泛型创建某个数组时

def test[T]() {
    val a = new Array[T] //运行时类型缺失,无法创建对应的java数组
}

因为java里的数组,实际会对应int[],float[],char[],等8个基础类型数组,与1个支持协变的引用类型数组。

更细的看一下java引用类型的数组类型:

scala> class A { 
|       val a1 = new Array[java.lang.Object](1);
|       val a2 = new Array[String](1);
| }

scala> :javap A    
...
//  a1:[Ljava/lang/Object;
...
//  a2:[Ljava/lang/String;

看到String[]会被编译器翻译为[Ljava/lang/String,而Object[]被翻译为[Ljava/lang/Object,其实可以看做是一种泛型类型的特例,[L 是类型构造器,StringObject 等是类型参数。但不同于java里其他泛型集合的实现,数组类型中的类型参数在运行时是必须的,即 [Ljava/lang/String[Ljava/lang/Object 是两个不同的类型,不像 Collection<String>Collection<StringBuilder> 运行时参数类型都被擦拭掉,成为Collection<Object>

为此,2.8中不得不引入了Manifest来保存T在运行时的类型信息(参考上一篇对Manifest与TypeTag的介绍),从而保证运行时scala数组与java的数组实现一致。

scala类型系统:20) 数组类型》上有3条评论

  1. hongjiang 文章作者

    // 补充一段采访
    Artima:您能否就Java的协变数组详细说明一下该问题?

    Martin Odersky:当Java刚出现时,Bill Joy和James Gosling以及其他Java组成员都认为,Java应该有泛型,只是他们没有足够的时间做出详细设计。所以由于Java中没有泛型,至少最初阶段没有,他们就认为,数组不得不是协变的。例如,这意味着一个字符串(String)数组是一个对象(Object)数组的子类型。其原因是他们希望能够重写,比如,一个“通用”排序方法,采用了一个对象数组和一个用来排序该数组的比较器,然后让你传送一个字符串数组的参数给它。通常情况下这属于类型不健全。这就是为什么在Java中你会获得一个数组存储例外。这实际上也证明,这种同样的事情引起了对于数组泛型实现的需求。这就是为什么在Java中泛型并不好使。你不能定义一个字符串的列表数组,这是不可能的。你只能被迫使用难看的原始类型,永远都只能是一个列表数组。因此,这有点类似原罪。他们对此做出了非常迅速的回应,认为这是一个快速破解。但随后实际上每一个设计决定都被毁灭了。因此,为了不陷入同样的陷阱,我们不得不中断,并提出现在我们将不向上兼容Java,我们也想做一些不同的事情。

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注