scala 里的对象
http://dblab.xmu.edu.cn/blog/953-2/
Scala中的继承与Java有着显著的不同:
(1)重写一个非抽象方法必须使用override修饰符。
(2)只有主构造器可以调用超类的主构造器。
(3)在子类中重写超类的抽象方法时,不需要使用override关键字。
(4)可以重写超类中的字段。
抽象类:
abstract class Car{ //是抽象类,不能直接被实例化
val carBrand: String //字段没有初始化值,就是一个抽象字段
def info() //抽象方法,不需要使用abstract关键字
def greeting() {println("Welcome to my car!")}
}
(1)定义一个抽象类,需要使用关键字abstract。
(2)定义一个抽象类的抽象方法,也不需要关键字abstract,只要把方法体空着,不写方法体就可以。
(3)抽象类中定义的字段,只要没有给出初始化值,就表示是一个抽象字段,但是,抽象字段必须要声明类型,
比如:val carBrand: String,就把carBrand声明为字符串类型,这个时候,不能省略类型,否则编译会报错。
扩展类:
抽象类不能直接被实例化,所以,下面我们定义几个扩展类
class BMWCar extends Car {
override val carBrand = "BMW" //重写超类字段,需要使用override关键字,否则编译会报错
def info() {printf("This is a %s car. It is on sale", carBrand)} //重写超类的抽象方法时,不需要使用override关键字,不过,如果加上override编译也不错报错
override def greeting() {println("Welcome to my BMW car!")} //重写超类的非抽象方法,必须使用override关键字
}
class BYDCar extends Car {
override val carBrand = "BYD" //重写超类字段,需要使用override关键字,否则编译会报错
def info() {printf("This is a %s car. It is cheap.", carBrand)} //重写超类的抽象方法时,不需要使用override关键字,不过,如果加上override编译也不错报错
override def greeting() {println("Welcome to my BYD car!")} //重写超类的非抽象方法,必须使用override关键字
}
object MyCar {
def main(args: Array[String]){
val myCar1 = new BMWCar()
val myCar2 = new BYDCar()
myCar1.greeting()
myCar1.info()
myCar2.greeting()
myCar2.info()
}
}
特质
Java中提供了接口,允许一个类实现任意数量的接口。在Scala中没有接口的概念,而是提供了“特质(trait)”,
它不仅实现了接口的功能,还具备了很多其他的特性。Scala的特质,是代码重用的基本单元,可以同时拥有抽
象方法和具体方法。Scala中,一个类只能继承自一个超类,却可以实现多个特质,从而重用特质中的方法和字段,
实现了多重继承。
特质的定义和类的定义非常相似,有区别的是,特质定义使用关键字trait。
trait CarId{
var id: Int
def currentId(): Int //定义了一个抽象方法
}
上面定义了一个特质,里面包含一个抽象字段id和抽象方法currentId。注意,抽象方法不需要使用abstract关键字,
特质中没有方法体的方法,默认就是抽象方法。
特质可以包含具体实现
trait CarGreeting{
def greeting(msg: String) {println(msg)}
}
特质定义好以后,就可以使用extends或with关键字把多个特质混入类中。
trait CarId{
var id: Int
def currentId(): Int //定义了一个抽象方法
}
trait CarGreeting{
def greeting(msg: String) {println(msg)}
}
class BYDCarId extends CarId with CarGreeting{ //使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质
override var id = 10000 //BYD汽车编号从10000开始
def currentId(): Int = {id += 1; id} //返回汽车编号
}
class BMWCarId extends CarId with CarGreeting{ //使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质
override var id = 20000 //BMW汽车编号从10000开始
def currentId(): Int = {id += 1; id} //返回汽车编号
}
object MyCar {
def main(args: Array[String]){
val myCarId1 = new BYDCarId()
val myCarId2 = new BMWCarId()
myCarId1.greeting("Welcome my first car.")
printf("My first CarId is %d.\n",myCarId1.currentId)
myCarId2.greeting("Welcome my second car.")
printf("My second CarId is %d.\n",myCarId2.currentId)
}
}
模式匹配
http://dblab.xmu.edu.cn/blog/956-2/
函数定义和高阶函数
http://dblab.xmu.edu.cn/blog/958-2/
列表的遍历
val list = List(1, 2, 3, 4, 5)
list.foreach(elem => println(elem)) //本行语句甚至可以简写为list.foreach(println),或者写成:list foreach println
映射的遍历
val university = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University","PKU"->"Peking University")
for ((k,v) <- 映射) 语句块
for ((k,v) <- university) printf("Code is : %s and name is: %s\n",k,v)
for (k<-university.keys) println(k)
for (v<-university.values) println(v)
university foreach {case(k,v) => println(k+":"+v)} //或者写成:university.foreach({case (k,v) => println(k+":"+v)})
university foreach {kv => println(kv._1+":"+kv._2)}
map操作
map操作是针对集合的典型变换操作,它将某个函数应用到集合中的每个元素,并产生一个结果集合。
val books = List("Hadoop", "Hive", "HDFS")
books.map(s => s.toUpperCase)
至于下面的表达式:
s => s.toUpperCase
这种表达式被称为“Lamda表达式”,在Java8以后有引入这种新的特性,Scala也拥有该特性。(备注:前面的“函数式编程”内容有讲到Lambda表达式)
“Lambda表达式”的形式如下:
(参数) => 表达式 //如果参数只有一个,参数的圆括号可以省略
可以看出,Lambda表达式实际上是一种匿名函数,大大简化代码编写工作。s => s.toUpperCase,它的含义是,对于输入s,都都执行s.toUpperCase操作。
flatMap操作
flatMap是map的一种扩展。在flatMap中,我们会传入一个函数,该函数对每个输入都会返回一个集合(而不是一个元素),然后,flatMap把生成的多个集合“拍扁”成为一个集合。
val books = List("Hadoop","Hive","HDFS")
books flatMap (s => s.toList)
上面的flatMap执行时,会把books中的每个元素都调用toList,生成List[Char],最终,多个Char的集合被“拍扁”成一个集合
a 方法 b
a.方法(b)
上面二者是等价的。所以:
books flatMap (s => s.toList)
books.flatMap(s => s.toList)
这条语句的含义就是:对于列表books中的每个元素,都执行Lambda表达式定义的匿名函数“s => s.toList”,把一个books元素转换成一个字符集合,
比如说,把“Hadoop”这个字符串转换成字符集合List(‘H’,’a’,’d’,’o’,’o’,’p’),把“Hive”字符串转换成字符集合List(‘H’,’i’,’v’,’e’)。
最后,flatMap把这些集合中的元素“拍扁”得到一个集合List(‘H’, ‘a’,’d’, ‘o’, ‘o’, ‘p’, ‘H’, ‘i’, ‘v’, ‘e’, ‘H’, ‘D’, ‘F’, ‘S’)。
reduce操作
在Scala中,我们可以使用reduce这种二元操作对集合中的元素进行归约。
reduce包含reduceLeft和reduceRight两种操作,前者从集合的头部开始操作,后者从集合的尾部开始操作。
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.reduceLeft(_ + _)
res21: Int = 15
scala> list.reduceRight(_ + _)
res22: Int = 15
可以看出,reduceLeft和reduceRight都是针对两两元素进行操作,在上面代码中,reduceLeft(_ + _)表示从列表头部开始,
对两两元素进行求和操作,下划线是占位符,用来表示当前获取的两个元素,两个下划线之间的是操作符,表示对两个元素进行的操作,
这里是加法操作(你也可以使用乘法*或者减法-等其他操作)。
实际上,我们可以直接使用reduce,而不用reduceLeft和reduceRight,这时,默认采用的是reduceLeft
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.reduce(_ - _)
res29: Int = -13 //可以看出,得到的结果和reduceLeft的结果是一样的
减法可以明显区分出reduceLeft和reduceRight
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.reduceLeft(_ - _) 1-2=-1 -1-3=-4 -4-4=-8 -8-5=-13
res25: Int = -13
scala> list.reduceRight(_ - _) 4-5=-1 3-(-1)=4 2-4=-2 1-(-2)=3
res26: Int = 3
fold操作
折叠(fold)操作和reduce(归约)操作比较类似。fold操作需要从一个初始的“种子”值开始,并以该值作为上下文,处理集合中的每个元素。
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.fold(10)(_*_)
res0: Int = 1200
可以看出,fold函数实现了对list中所有元素的累乘操作。fold函数需要两个参数,一个参数是初始种子值,这里是10,另一个参数是用于
计算结果的累计函数,这里是累乘。执行list.fold(10)(*)时,首先把初始值拿去和list中的第一个值1做乘法操作,得到累乘值10,然后
再拿这个累乘值10去和list中的第2个值2做乘法操作,得到累乘值20,依此类推,一直得到最终的累乘结果1200。
fold有两个变体:foldLeft()和foldRight(),其中,foldLeft(),第一个参数为累计值,集合遍历的方向是从左到右。foldRight(),
第二个参数为累计值,集合遍历的方向是从右到左。对于fold()自身而言,遍历的顺序是未定义的,不过,一般都是从左到右遍历。