(原文连接:http://www.yangbajing.me/2016/07/24/写给java程序员的scala入门教程/,转载请注明)java
以前由于Spark的引入,写了一篇《写给Python程序员的Scala入门教程》。那篇文章简单对比了Scala与Python的异同,并介绍了一些Scala的经常使用编程技巧。今天这篇文章将面向广大的Java程序员,带领Javaer进入函数式编程的世界。python
Java 8拥有了一些初步的函数式编程能力:闭包等,还有新的并发编程模型及Stream这个带高阶函数和延迟计算的数据集合。在尝试了Java 8之后,也许会以为意犹未尽。是的,你会发现Scala能知足你在初步尝试函数式编程后那求知的欲望。程序员
到Scala官方下载地址下载:http://scala-lang.org/download/:es6
wget -c http://downloads.lightbend.com/scala/2.11.8/scala-2.11.8.tgz tar zxf scala-2.11.8.tgz cd scala-2.11.8 ./bin/scala Welcome to Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60). Type in expressions to have them evaluated. Type :help for more information. scala>
RELP正则表达式
刚才咱们已经启动了Scala RELP,它是一个基于命令行的交互式编程环境。对于有着Python、Ruby等动态语言的同窗来讲,这是一个很经常使用和工具。但Javaer们第一次见到会以为比较神奇。咱们能够在RELP中作一些代码尝试而不用启动笨拙的IDE,这在咱们思考问题时很是的方便。对于Javaer有一个好消息,JDK 9干始将内建支持RELP功能。spring
对于Scala经常使用的IDE(集成开发环境),推荐使用IDEA for scala plugins和scala-ide。express
Scala的强大,除了它自身对多核编程更好的支持、函数式特性及一些基于Scala的第3方库和框架(如:Akka、Playframework、Spark、Kafka……),还在于它能够无缝与Java结合。全部为Java开发的库、框架均可以天然的融入Scala环境。固然,Scala也能够很方便的Java环境集成,好比:Spring。若你须要第3方库的支持,可使用Maven、Gradle、Sbt等编译环境来引入。apache
Scala是一个面向对象的函数式特性编程语言,它继承了Java的面向对特性,同时又从Haskell
等其它语言那里吸取了不少函数式特性并作了加强。编程
Scala中变量不须要显示指定类型,但须要提早声明。这能够避免不少命名空间污染问题。Scala有一个很强大的类型自动推导功能,它能够根据右值及上下文自动推导出变量的类型。你能够经过以下方式来直接声明并赋值。安全
scala> val a = 1 a: Int = 1 scala> val b = true b: Boolean = true scala> val c = 1.0 c: Double = 1.0 scala> val a = 30 + "岁" a: String = 30岁
Immutable
(注:函数式编程有一个很重要的特性:不可变性。Scala中除了变量的不可变性,它还定义了一套不可变集合scala.collection.immutable._
。)
val
表明这是一个final variable,它是一个常量。定义后就不能够改变,相应的,使用var
定义的就是日常所见的变量了,是能够改变的。从终端的打印能够看出,Scala从右值自动推导出了变量的类型。Scala能够如动态语言似的编写代码,但又有静态语言的编译时检查。这对于Java中冗长、重复的类型声明来讲是一种很好的进步。
(注:在RELP
中,val
变量是能够从新赋值的,这是`RELP`的特性。在日常的代码中是不能够的。)
基础数据类型
Scala中基础数据类型有:Byte、Short、Int、Long、Float、Double,Boolean,Char、String。和Java不一样的是,Scala中没在区分原生类型和装箱类型,如:int
和Integer
。它统一抽象成Int
类型,这样在Scala中全部类型都是对象了。编译器在编译时将自动决定使用原生类型仍是装箱类型。
字符串
Scala中的字符串有3种。
scala> val c2 = '杨' c2: Char = 杨 scala> val s1 = "重庆誉存企业信用管理有限公司" s1: String = 重庆誉存企业信用管理有限公司 scala> val s2 = s"重庆誉存企业信用管理有限公司${c2}景" s2: String = 重庆誉存企业信用管理有限公司 scala> val s3 = s"""重庆誉存企业信用管理有限公司"工程师"\n${c2}景是江津人""" s3: String = 重庆誉存企业信用管理有限公司"工程师" 杨景是江津人
Scala中的运算符实际上是定义在对象上的方法(函数),你看到的诸如:3 + 2
实际上是这样子的:3.+(2)
。+
符号是定义在Int
对象上的一个方法。支持和Java一至的运算符(方法):
(注:在Scala中,方法前的.
号和方法两边的小括号在不引发歧义的状况下是能够省略的。这样咱们就能够定义出很优美的DSL
)
==
、!=
:比较运算!
、|
、&
、^
:逻辑运算>>
、<<
:位运算注意
在Scala中,修正了(算更符合通常人的常规理解吧)==
和!=
运算符的含义。在Scala中,==
和!=
是执行对象的值比较,至关于Java中的equals
方法(实际上编译器在编译时也是这么作的)。而对象的引用比较须要使用eq
和ne
两个方法来实现。
Scala中支持if
、while
、for comprehension
(for表达式)、match case
(模式匹配)四大主要控制语句。Scala不支持switch
和? :
两种控制语句,但它的if
和match case
会有更好的实现。
if
Scala支持if
语句,其基本使用和Java
、Python
中的同样。但不一样的时,它是有返回值的。
(注:Scala是函数式语言,函数式语言还有一大特性就是:表达式。函数式语言中全部语句都是基于“表达式”的,而“表达式”的一个特性就是它会有一个值。全部像Java
中的? :
3目运算符可使用if
语句来代替)。
scala> if (true) "真" else "假" res0: String = 真 scala> val f = if (false) "真" else "假" f: String = 假 scala> val unit = if (false) "真" unit: Any = () scala> val unit2 = if (true) "真" unit2: Any = 真
能够看到,if
语句也是有返回值的,将表达式的结果赋给变量,编译器也能正常推导出变量的类型。unit
和unit2
变量的类型是Any
,这是由于else
语句的缺失,Scala编译器就按最大化类型来推导,而Any
类型是Scala中的根类型。()
在Scala中是Unit
类型的实例,能够看作是Java
中的Void
。
while
Scala中的while
循环语句:
while (条件) { 语句块 }
for comprehension
Scala中也有for
表达式,但它和Java
中的for
不太同样,它具备更强大的特性。一般的for
语句以下:
for (变量 <- 集合) { 语句块 }
Scala中for
表达式除了上面那样的常规用法,它还可使用yield
关键字将集合映射为另外一个集合:
scala> val list = List(1, 2, 3, 4, 5) list: List[Int] = List(1, 2, 3, 4, 5) scala> val list2 = for (item <- list) yield item + 1 list2: List[Int] = List(2, 3, 4, 5, 6)
还能够在表达式中使用if
判断:
scala> val list3 = for (item <- list if item % 2 == 0) yield item list3: List[Int] = List(2, 4)
还能够作flatMap
操做,解析2维列表并将结果摊平(将2维列表拉平为一维列表):
scala> val llist = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) llist: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) scala> for { | l <- llist | item <- l if item % 2 == 0 | } yield item res3: List[Int] = List(2, 4, 6, 8)
看到了,Scala中for comprehension
的特性是很强大的。Scala的整个集合库都支持这一特性,包括:Seq
、Map
、Set
、Array
……
Scala没有C-Like语言里的for (int i = 0; i < 10; i++)
语法,但Range
(范围这个概念),能够基于它来实现循环迭代功能。在Scala中的使用方式以下:
scala> for (i <- (0 until 10)) { | println(i) | } 0 1 2 3 4 5 6 7 8 9
Scala中还有一个to
方法:
scala> for (i <- (0 to 10)) print(" " + i) 0 1 2 3 4 5 6 7 8 9 10
你还能够控制每次步进的步长,只须要简单的使用by
方法便可:
scala> for (i <- 0 to 10 by 2) print(" " + i) 0 2 4 6 8 10
match case
模式匹配,是函数式语言很强大的一个特性。它比命令式语言里的switch
更好用,表达性更强。
scala> def level(s: Int) = s match { | case n if n >= 90 => "优秀" | case n if n >= 80 => "良好" | case n if n >= 70 => "良" | case n if n >= 60 => "及格" | case _ => "差" | } level: (s: Int)String scala> level(51) res28: String = 差 scala> level(93) res29: String = 优秀 scala> level(80) res30: String = 良好
能够看到,模式匹配能够实现switch
类似的功能。但与switch
须要使用break
明确告知终止以后的判断不一样,Scala中的match case
是默认break的。只要其中一个case
语句匹配,就终止以后的因此比较。且对应case
语句的表达式值将做为整个match case
表达式的值返回。
Scala中的模式匹配还有类型匹配、数据抽取、谓词判断等其它有用的功能。这里只作简单介绍,以后会单独一个章节来作较详细的解读。
在java.util
包下有丰富的集合库。Scala除了可使用Java定义的集合库外,它还本身定义了一套功能强大、特性丰富的scala.collection
集合库API。
在Scala中,经常使用的集合类型有:List
、Set
、Map
、Tuple
、Vector
等。
List
Scala中List
是一个不可变列表集合,它很精妙的使用递归结构定义了一个列表集合。
scala> val list = List(1, 2, 3, 4, 5) list: List[Int] = List(1, 2, 3, 4, 5)
除了以前使用List
object来定义一个列表,还可使用以下方式:
scala> val list = 1 :: 2 :: 3 :: 4 :: 5 :: Nil list: List[Int] = List(1, 2, 3, 4, 5)
List
采用前缀操做的方式(全部操做都在列表顶端(开头))进行,::
操做符的做用是将一个元素和列表链接起来,并把元素放在列表的开头。这样List
的操做就能够定义成一个递归操做。添加一个元素就是把元素加到列表的开头,List只须要更改下头指针,而删除一个元素就是把List的头指针指向列表中的第2个元素。这样,List
的实现就很是的高效,它也不须要对内存作任何的转移操做。List
有不少经常使用的方法:
scala> list.indexOf(3) res6: Int = 2 scala> 0 :: list res8: List[Int] = List(0, 1, 2, 3, 4, 5) scala> list.reverse res9: List[Int] = List(5, 4, 3, 2, 1) scala> list.filter(item => item == 3) res11: List[Int] = List(3) scala> list res12: List[Int] = List(1, 2, 3, 4, 5) scala> val list2 = List(4, 5, 6, 7, 8, 9) list2: List[Int] = List(4, 5, 6, 7, 8, 9) scala> list.intersect(list2) res13: List[Int] = List(4, 5) scala> list.union(list2) res14: List[Int] = List(1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9) scala> list.diff(list2) res15: List[Int] = List(1, 2, 3)
Scala中默认都是Immutable collection,在集合上定义的操做都不会更改集合自己,而是生成一个新的集合。这与Java集合是一个根本的区别,Java集合默认都是可变的。
Tuple
Scala中也支持Tuple(元组)这种集合,但最多只支持22个元素(事实上Scala中定义了Tuple0
、Tuple1
……Tuple22
这样22个TupleX
类,实现方式与C++ Boost
库中的Tuple
相似)。和大多数语言的Tuple相似(好比:Python),Scala也采用小括号来定义元组。
scala> val tuple1 = (1, 2, 3) tuple1: (Int, Int, Int) = (1,2,3) scala> tuple1._2 res17: Int = 2 scala> val tuple2 = Tuple2("杨", " ) tuple2: (String, String) = (杨,景)
可使用xxx._[X]
的形式来引用Tuple
中某一个具体元素,其_[X]
下标是从1开始的,一直到22(如有定义这么多)。
Set
Set
是一个不重复且无序的集合,初始化一个Set
须要使用Set
对象:
scala> val set = Set("Scala", "Java", "C++", "Javascript", "C#", "Python", "PHP") set: scala.collection.immutable.Set[String] = Set(Scala, C#, Python, Javascript, PHP, C++, Java) scala> set + "Go" res21: scala.collection.immutable.Set[String] = Set(Scala, C#, Go, Python, Javascript, PHP, C++, Java) scala> set filterNot (item => item == "PHP") res22: scala.collection.immutable.Set[String] = Set(Scala, C#, Python, Javascript, C++, Java)
Map
Scala中的Map
默认是一个HashMap,其特性与Java版的HashMap
基本一至,除了它是Immutable
的:
scala> val map = Map("a" -> "A", "b" -> "B") map: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B) scala> val map2 = Map(("b", "B"), ("c", "C")) map2: scala.collection.immutable.Map[String,String] = Map(b -> B, c -> C)
Scala中定义Map
时,传入的每一个Entry
(K、V对)其实就是一个Tuple2
(有两个元素的元组),而->
是定义Tuple2
的一种便捷方式。
scala> map + ("z" -> "Z") res23: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B, z -> Z) scala> map.filterNot(entry => entry._1 == "a") res24: scala.collection.immutable.Map[String,String] = Map(b -> B) scala> val map3 = map - "a" map3: scala.collection.immutable.Map[String,String] = Map(b -> B) scala> map res25: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B)
Scala的immutable collection并无添加和删除元素的操做,其定义+
(List
使用::
在头部添加)操做都是生成一个新的集合,而要删除一个元素通常使用 -
操做直接将Key从map
中减掉便可。
(注:Scala中也scala.collection.mutable._
集合,它定义了不可变集合的相应可变集合版本。通常状况下,除非一此性能优先的操做(其实Scala集合采用了共享存储的优化,生成一个新集合并不会生成全部元素的复本,它将会和老的集合共享大元素。由于Scala中变量默认都是不可变的),推荐仍是采用不可变集合。由于它更直观、线程安全,你能够肯定你的变量不会在其它地方被不当心的更改。)
Scala里也有class
关键字,不过它定义类的方式与Java有些区别。Scala中,类默认是public的,且类属性和方法默认也是public的。Scala中,每一个类都有一个**“主构造函数”**,主构造函数相似函数参数同样写在类名后的小括号中。由于Scala没有像Java那样的“构造函数”,因此属性变量都会在类被建立后初始化。因此当你须要在构造函数里初始化某些属性或资源时,写在类中的属性变量就至关于构造初始化了。
在Scala中定义类很是简单:
class Person(name: String, val age: Int) { override def toString(): String = s"姓名:$name, 年龄: $age" }
默认,Scala主构造函数定义的属性是private的,能够显示指定:val
或var
来使其可见性为:public。
Scala中覆写一个方法必需添加:override
关键字,这对于Java来讲能够是一个修正。当标记了override
关键字的方法在编译时,若编译器未能在父类中找到可覆写的方法时会报错。而在Java中,你只能经过@Override
注解来实现相似功能,它的问题是它只是一个可选项,且编译器只提供警告。这样你仍是很容易写出错误的“覆写”方法,你之后覆写了父类函数,但其实颇有可能你是实现了一个新的方法,从而引入难以察觉的BUG。
实例化一个类的方式和Java同样,也是使用new
关键字。
scala> val me = new Person("杨景", 30) me: Person = 姓名:杨景, 年龄: 30 scala> println(me) 姓名:杨景, 年龄: 30 scala> me.name <console>:20: error: value name is not a member of Person me.name ^ scala> me.age res11: Int = 30
case class(样本类)
case class
是Scala中学用的一个特性,像Kotlin
这样的语言也学习并引入了相似特性(在Kotlin
中叫作:data class
)。case class
具备以下特性:
new
关键词建立,直接使用类名便可var
、private
等特性,但通常不推荐这样用equals
、hashcode
、toString
等函数Serializable
接口,默认是可序列化的copy
方法,能够方便的根据某个case class
实例来生成一个新的实例这里给出一个case class
的使用样例:
scala> trait Person defined trait Person scala> case class Man(name: String, age: Int) extends Person defined class Man scala> case class Woman(name: String, age: Int) extends Person defined class Woman scala> val man = Man("杨景", 30) man: Man = Man(杨景,30) scala> val woman = Woman("女人", 23) woman: Woman = Woman(女人,23) scala> val manNextYear = man.copy(age = 31) manNextYear: Man = Man(杨景,31)
Scala有一种不一样于Java的特殊类型,Singleton Objects。
object Blah { def sum(l: List[Int]): Int = l.sum }
在Scala中,没有Java里的static静态变量和静态做用域的概念,取而代之的是:object。它除了能够实现Java里static的功能,它同时仍是一个线程安全的单例类。
伴身对象
大多数的object
都不是独立的,一般它都会与一个同名的class
定义在一块儿。这样的object
称为伴身对象。
class IntPair(val x: Int, val y: Int) object IntPair { import math.Ordering implicit def ipord: Ordering[IntPair] = Ordering.by(ip => (ip.x, ip.y)) }
注意
伴身对象必需和它关联的类定义定义在同一个**.scala**文件。
伴身对象和它相关的类之间能够相互访问受保护的成员。在Java程序中,不少时候会把static成员设置成private的,在Scala中须要这样实现此特性:
class X { import X._ def blah = foo } object X { private def foo = 42 }
在Scala中,函数是一等公民。函数能够像类型同样被赋值给一个变量,也能够作为一个函数的参数被传入,甚至还能够作为函数的返回值返回。
从Java 8开始,Java也具有了部分函数式编程特性。其Lamdba函数容许将一个函数作值赋给变量、作为方法参数、作为函数返回值。
在Scala中,使用def
关键ygnk来定义一个函数方法:
scala> def calc(n1: Int, n2: Int): (Int, Int) = { | (n1 + n2, n1 * n2) | } calc: (n1: Int, n2: Int)(Int, Int) scala> val (add, sub) = calc(5, 1) add: Int = 6 sub: Int = 5
这里定义了一个函数:calc
,它有两个参数:n1
和n2
,其类型为:Int
。cala
函数的返回值类型是一个有两个元素的元组,在Scala中能够简写为:(Int, Int)
。在Scala中,代码段的最后一句将作为函数返回值,因此这里不须要显示的写return
关键字。
而val (add, sub) = calc(5, 1)
一句,是Scala中的抽取功能。它直接把calc
函数返回的一个Tuple2
值赋给了add
他sub
两个变量。
函数能够赋给变量:
scala> val calcVar = calc _ calcVar: (Int, Int) => (Int, Int) = <function2> scala> calcVar(2, 3) res4: (Int, Int) = (5,6) scala> val sum: (Int, Int) => Int = (x, y) => x + y sum: (Int, Int) => Int = <function2> scala> sum(5, 7) res5: Int = 12
在Scala中,有两种定义函数的方式:
val calcVar = calc _
。下划线在此处的含意是将函数赋给了变量,函数自己的参数将在变量被调用时再传入。val sum: (Int, Int) => Int = (x, y) => x + y
,在冒号以后,等号以前部分:(Int, Int) => Int
是函数签名,表明sum
这个函数值接收两个Int类型参数并返回一个Int类型参数。等号以后部分是函数体,在函数函数时,x
、y
参数类型及返回值类型在此能够省略。一个函数示例:自动资源管理
在咱们的平常代码中,资源回收是一个很常见的操做。在Java 7以前,咱们必需写不少的try { ... } finally { xxx.close() }
这样的样版代码来手动回收资源。Java 7开始,提供了try with close这样的自动资源回收功能。Scala并不能使用Java 7新加的try with close资源自动回收功能,但Scala中有很方便的方式实现相似功能:
def using[T <: AutoCloseable, R](res: T)(func: T => R): R = { try { func(res) } finally { if (res != null) res.close() } } val allLine = using(Files.newBufferedReader(Paths.get("/etc/hosts"))) { reader => @tailrec def readAll(buffer: StringBuilder, line: String): String = { if (line == null) buffer.toString else { buffer.append(line).append('\n') readAll(buffer, reader.readLine()) } } readAll(new StringBuilder(), reader.readLine()) } println(allLine)
using
是咱们定义的一个自动化资源管帮助函数,它接爱两个参数化类型参数,一个是实现了AutoCloseable
接口的资源类,一个是形如:T => R
的函数值。func
是由用户定义的对res
进行操做的函数代码体,它将被传给using
函数并由using
代执行。而res
这个资源将在using
执行完成返回前调用finally
代码块执行.close
方法来清理打开的资源。
这个:T <: AutoCloseable
范型参数限制了T类型必需为AutoCloseable
类型或其子类。R
范型指定using
函数的返回值类型将在实际调用时被自动参数化推导出来。咱们在Scala Console中参看allLine
变量的类型能够看到 allLine
将被正确的赋予String类型,由于咱们传给using
函数参数func
的函数值返回类型就为String:
scala> :type allLine String
在readAll
函数的定义处,有两个特别的地方:
@tailrec
注解在Scala中,由于函数是第一类的,它能够被赋值给一个变量。因此Scala中的def
定义函数能够等价val func = (x: Int, y: Int) => x + y
这个的函数字面量定义函数形式。因此,既然经过变量定义的函数能够放在其它函数代码体内,经过def
定义的函数也同样能够放在其它代码体内,这和Javascript很像。
@tailrec
注解的含义是这个函数是尾递归函数,编译器在编译时将对其优化成相应的while循环。若一个函数不是尾递归的,加上此注解在编译时将报错。
模式匹配是函数式编程里面很强大的一个特性。
以前已经见识过了模式匹配的简单使用方式,能够用它替代:if else、switch这样的分支判断。除了这些简单的功能,模式匹配还有一系列强大、易用的特性。
match 中的值、变量和类型
scala> for { | x <- Seq(1, false, 2.7, "one", 'four, new java.util.Date(), new RuntimeException("运行时异常")) | } { | val str = x match { | case d: Double => s"double: $d" | case false => "boolean false" | case d: java.util.Date => s"java.util.Date: $d" | case 1 => "int 1" | case s: String => s"string: $s" | case symbol: Symbol => s"symbol: $symbol" | case unexpected => s"unexpected value: $unexpected" | } | println(str) | } int 1 boolean false double: 2.7 string: one symbol: 'four java.util.Date: Sun Jul 24 16:51:20 CST 2016 unexpected value: java.lang.RuntimeException: 运行时异常
上面小试牛刀校验变量类型的同时完成类型转换功能。在Java中,你确定写过或见过以下的代码:
public void receive(message: Object) { if (message isInstanceOf String) { String strMsg = (String) message; .... } else if (message isInstanceOf java.util.Date) { java.util.Date dateMsg = (java.util.Date) message; .... } .... }
对于这样的代码,真是辣眼睛啊~~~。
序列的匹配
scala> val nonEmptySeq = Seq(1, 2, 3, 4, 5) scala> val emptySeq = Seq.empty[Int] scala> val emptyList = Nil scala> val nonEmptyList = List(1, 2, 3, 4, 5) scala> val nonEmptyVector = Vector(1, 2, 3, 4, 5) scala> val emptyVector = Vector.empty[Int] scala> val nonEmptyMap = Map("one" -> 1, "two" -> 2, "three" -> 3) scala> val emptyMap = Map.empty[String, Int] scala> def seqToString[T](seq: Seq[T]): String = seq match { | case head +: tail => s"$head +: " + seqToString(tail) | case Nil => "Nil" | } scala> for (seq <- Seq( | nonEmptySeq, emptySeq, nonEmptyList, emptyList, | nonEmptyVector, emptyVector, nonEmptyMap.toSeq, emptyMap.toSeq)) { | println(seqToString(seq)) | } 1 +: 2 +: 3 +: 4 +: 5 +: Nil Nil 1 +: 2 +: 3 +: 4 +: 5 +: Nil Nil 1 +: 2 +: 3 +: 4 +: 5 +: Nil Nil (one,1) +: (two,2) +: (three,3) +: Nil Nil
模式匹配能很方便的抽取序列的元素,seqToString
使用了模式匹配以递归的方式来将序列转换成字符串。case head +: tail
将序列抽取成“头部”和“非头部剩下”两部分,head
将保存序列第一个元素,tail
保存序列剩下部分。而case Nil
将匹配一个空序列。
case class的匹配
scala> trait Person scala> case class Man(name: String, age: Int) extends Person scala> case class Woman(name: String, age: Int) extends Person scala> case class Boy(name: String, age: Int) extends Person scala> val father = Man("父亲", 33) scala> val mather = Woman("母亲", 30) scala> val son = Man("儿子", 7) scala> val daughter = Woman("女儿", 3) scala> for (person <- Seq[Person](father, mather, son, daughter)) { | person match { | case Man("父亲", age) => println(s"父亲今年${age}岁") | case man: Man if man.age < 10 => println(s"man is $man") | case Woman(name, 30) => println(s"${name}今年有30岁") | case Woman(name, age) => println(s"${name}今年有${age}岁") | } | } 父亲今年33岁 母亲今年有30岁 man is Man(儿子,7) 女儿今年有3岁
在模式匹配中对case class
进行解构操做,能够直接提取出感兴趣的字段并赋给变量。同时,模式匹配中还可使用guard语句,给匹配判断添加一个if
表达式作条件判断。
Scala是对多核和并发编程的支付作得很是好,它的Future类型提供了执行异步操做的高级封装。
Future对象完成构建工做之后,控制权便会马上返还给调用者,这时结果还不能够马上可用。Future实例是一个句柄,它指向最终可用的结果值。不论操做成功与否,在future操做执行完成前,代码均可以继续执行而不被阻塞。Scala提供了多种方法用于处理future。
scala> :paste // Entering paste mode (ctrl-D to finish) import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} import scala.concurrent.ExecutionContext.Implicits.global val futures = (0 until 10).map { i => Future { val s = i.toString print(s) s } } val future = Future.reduce(futures)((x, y) => x + y) val result = Await.result(future, Duration.Inf) // Exiting paste mode, now interpreting. 0132564789 scala> val result = Await.result(future, Duration.Inf) result: String = 0123456789
上面代码建立了10个Future
对象,Future.apply
方法有两个参数列表。第一个参数列表包含一个须要并发执行的命名方法体(by-name body);而第二个参数列表包含了隐式的ExecutionContext
对象,能够简单的把它看做一个线程池对象,它决定了这个任务将在哪一个异步(线程)执行器中执行。futures
对象的类型为IndexedSeq[Future[String]]
。本示例中使用Future.reduce
把一个futures
的IndexedSeq[Future[String]]
类型压缩成单独的Future[String]
类型对象。Await.result
用来阻塞代码并获取结果,输入的Duration.Inf
用于设置超时时间,这里是无限制。
这里能够看到,在Future
代码内部的println
语句打印输出是无序的,但最终获取的result
结果倒是有序的。这是由于虽然每一个Future
都是在线程中无序执行,但Future.reduce
方法将按传入的序列顺序合并结果。
除了使用Await.result
阻塞代码获取结果,咱们还可使用事件回调的方式异步获取结果。Future
对象提供了几个方法经过回调将执行的结果返还给调用者,经常使用的有:
import scala.concurrent.Future import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global val futures = (1 to 2) map { case 1 => Future.successful("1是奇数") case 2 => Future.failed(new RuntimeException("2不是奇数")) } futures.foreach(_.onComplete { case Success(i) => println(i) case Failure(t) => println(t) }) Thread.sleep(2000)
futures.onComplete
方法是一个偏函数,它的参数是:Try[String]
。Try
有两个子类,成功是返回Success[String]
,失败时返回Failure[Throwable]
,能够经过模式匹配的方式获取这个结果。
本篇文章简单的介绍了Scala的语言特性,本文并不仅限于Java程序员,任何有编程经验的程序员均可以看。如今你应该对Scala有了一个基础的认识,并能够写一些简单的代码了。我在博客中分享了一些《Scala实战(系列)》文章,介绍更函数式的写法及与实际工程中结合的例子。也欢迎对Scala感兴趣的同窗与我联系经,一块儿交流、学习。