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实例的容器。

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

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

时间: 2024-10-03 23:00:56

scala学习手记17 - 容器和类型推断的相关文章

scala学习手记20 - 方法返回类型推断

除了推演变量的类型,scala也会推演方法的返回类型.不过这里有一处需要注意:方法返回类型的推演依赖于方法的定义方式.如果用等号"="定义方法,scala就会推演方法返回类型:否则,它就认为方法的返回为void.看一个例子: def printMethodInfo(methodName: String) { println("The return type of " + methodName + " is " + getClass().getDe

scala学习手记36 - 容器基础

scala的容器包括Set.List和Map.三种容器的特征和Java中一样.scala为每种容器都提供了可变和不可变两种版本,分别位于scala.collection.mutable或scala.collection.immutable包下.scala建议使用尽量使用不可变容器,尤其是在多线程环境下.并且scala默认的容器就是不变的版本. 可变容器没什么好说的,java中的容器就是.来看一个不可变容器的实例: val colors1 = Set("Blue", "Gree

scala学习手记4 - Java基本类型对应的scala类

在Java中变量类型分为两大类:基本类型和引用类型.虽然在JDK1.5以后引入了自动装箱和自动拆箱机制,大大减少了我们在直接类型和引用类型之间的纠结,但仍有一些我们不得不考虑的问题.比如我在工作遇到的基本类型和其包装类型的默认值的问题,比如泛型只能使用引用类型,比如默认情况下直接类型的值不能作为对象来操作(1.toString()就不能通过编译)等等. 在scala中一切都是对象. 在scala中1.toString()这样的写法是完全可以接受的.java中的每个基本类型在scala中都有对应的

scala学习手记39 - 模式匹配

在java中有switch/case这样的模式匹配语句,可以匹配的类型包括int,byte,char,short, enum,在java8又支持了字符串. 在scala中也有类似的模式匹配语句,即match-case.这个好现在之前使用过一次.scala中的match-case匹配的类型更为广泛,它是对Any类型起作用的. 来看个例子: def activity(day: String) { day match { case "Sunday" => println("E

scala学习手记19 - Option类型

看到Option类型就知道这本教材应该要说那个了. 使用过guava后,应该知道guava中的Optional类的作用是什么.算了找下原始文档好了: Optional<T> is a way of replacing a nullable T reference with a non-null value. An Optional may either contain a non-null T reference (in which case we say the reference is &

scala学习手记23 - 函数值

scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子:从1到某个数求和.使用Java很容易实现: int sum(int max){ int result = 0; for (int i = 0; i <= max; i++) { result +=i; } return result; } 使用scala实现也没有多大区别. 现在再扩展下需求:对某

scala学习手记8 - 自适应的默认做法

scala有一些默认做法,会让代码更简洁.更易读写,下面列出了这样几个特性: 1. 支持脚本.scala支持脚本,因此无须将所有的代码都放到类里.如果脚本可以满足需求,就将代码放到一个脚本里,无须再创建一个冗余的类. 2. return是可选的.如果没有写return关键字,方法调用会自动返回最后一个求值的表达式--如果它符合方法声明的返回值类型. 3. 分号":"是可选的.不必在每个语句的后面都写上分号,这样会使代码更简洁.如果语句太长或者包含多行的话可以换行继续写,scala能够识

scala学习手记38 - 方法命名约定和for表达式

方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. 比如下面这个例子: class Cow { def ^(moon: Moon) = println("Cow jumped over the moon") } class Moon { def ^:(cow: Cow) = println("This cow jumped ove

scala学习手记9 - =和==

= 赋值运算 scala的赋值运算和java的有着很大的不同.如a=b这样的赋值运算,在Java中返回值是a的值,在scala中返回的则是Unit.Unit是值类型,全局只存在唯一的值,即(),通常Unit只用来声明函数或方法的返回值,其他场景基本是没有意义的.这样就很容易导致一些错误地使用,比如a=b=c这样的赋值运算在java中是绝对可以的,但是在scala中运行就会报错.看一段代码: var a, b, c = 1 a = b=c println(a) 上面这段代码还没有执行就在IDE中报