scala学习手记17 - 容器和类型推断

关于scala的类型推断前面已经提到过多次。再来看一下下面这个例子:

import java.util._

var list1: List[Int] = new ArrayList[Int]
var list2 = new ArrayList[Int]
list2 add 1
list2 add 2
var total = 0
for (index <- 0 until list2.size()) {
  total += list2.get(index)
}
println("Total is " + total)

在这段代码中新建了两个ArrayList实例。第一个实例list1的声明使用了显式却冗余的类型声明,第二个实例list2的声明则依赖了scala的类型推断。

再注意下第一行的导入语句,import语句里的下划线等价于java导入里的“*”。在scala导入语句中使用java.util._则表示导入java.util包下的所有类。如果scala导入语句中的下划线是跟在一个类名后,则表示导入类中的所有成员——等价于Java中的静态导入。

看一下程序的执行结果:

image

一方面scala提供了类型推断功能让我们可以简单地声明类实例;另一方面,scala在类型转换方面也显得非常警觉,严禁进行可能引发类型问题的转换,如下面的代码:

/**
  * Created by robin on 2016/6/21.
  */
import java.util._

var list1 = new ArrayList[Int]
var list2 = new ArrayList
list2 = list1 // Compilation Error

在代码中先是创建了一个ArrayList[Int]的实例list1,而后又创建了一个不带类型参数的ArrayList实例list2(实际上是创建了一个ArrayList[Nothing]的实例)。最后将list1赋值给list2,因为这一步,程序在编译时会报错,看一下:

image

如果是对应的Java代码在编译时不会报错,但是在执行时会抛出ClassCastException异常(因为Java的泛型实现机制)。

在scala中,Nothing是所有类的子类。父类是不能作为子类的实例的,所以这里会报错。

那么如何创建一个不指定类型的ArrayList实例呢?想想Java。使用Object是一个方向,但是在scala中却不甚规范。应该是在上一篇文中提到过:在scala中Any类是所有类的基类——如同Object在Java中的角色。

将上面的代码略作调整:

import java.util._

var list1 = new ArrayList[Int]
var list2 = new ArrayList[Any]

var ref1 : Int = 1
var ref2 : Any = null

ref2 = ref1 //OK

list2 = list1 // Compilation Error

在新的代码里list1是ArrayList[Int]的实例, list2是ArrayList[Any]的实例。此外还创建了两个新的实例:Int实例ref1、Any实例ref2。将ref1赋值给ref2是没有错的,这等价于将Integer的值赋给Object对象。但是将list1赋值list2却依然会报错。

image

学习scala的时候,将ArrayList等集合对象视为容器。Scala不允许将一个持有任意类型实例的容器赋给一个持有Any实例的容器。

这一节有如下三点比较重要:

  • 在使用scala时,只要是有意义的地方,都可以依赖类型推演。
  • Scala认为无参数化类型的容器是Nothing的容器,并且限制类型间的赋值。
  • 默认情况下,Scala不允许将一个持有任意类型实例的容器赋给一个持有Any实例的容器。

这几点结合起来,可以增强编译时的类型安全。

#############

posted @ 2016-06-21 22:51  robin·张  阅读(1331)  评论(0编辑  收藏  举报