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中的静态导入。
看一下程序的执行结果:
一方面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,因为这一步,程序在编译时会报错,看一下:
如果是对应的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却依然会报错。
学习scala的时候,将ArrayList等集合对象视为容器。Scala不允许将一个持有任意类型实例的容器赋给一个持有Any实例的容器。
这一节有如下三点比较重要:
- 在使用scala时,只要是有意义的地方,都可以依赖类型推演。
- Scala认为无参数化类型的容器是Nothing的容器,并且限制类型间的赋值。
- 默认情况下,Scala不允许将一个持有任意类型实例的容器赋给一个持有Any实例的容器。
这几点结合起来,可以增强编译时的类型安全。
#############
仅是学习笔记,难免出错,望不吝指点