搜尋
首頁Javajava教程寫給Java程式設計師的Scala入門教程

Java 8擁有了一些初步的函數式程式設計能力:閉包等,還有新的同時程式設計模型及Stream這個帶有高階函數和延遲計算的資料集合。在嘗試了Java 8以後,或許會覺得意猶未盡。是的,你會發現Scala能滿足你在初步嘗試函數式程式設計後那求知的慾望。

安裝Scala

到Scala官方下載地址下載:http://scala-lang.org/download/:

wget -c http://downloads.lightbend.com/scala/2.11.8/scala-2.11.8.tgztar 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功能。

對於Scala常用的IDE(整合開發環境),建議使用IDEA for scala plugins和scala-ide。

Scala的強大,除了它本身對多核心程式更好的支援、函數式特性及一些基於Scala的第3方函式庫與框架(如:Akka、Playframework、Spark、Kafka…),還在於它可以無縫與Java結合。所有為Java開發的函式庫、框架都可以自然的融入Scala環境。當然,Scala也可以很方便的Java環境集成,例如:Spring。若你需要第3方函式庫的支持,可以使用Maven、Gradle、Sbt等編譯環境來引入。

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種。

分別是普通字串,它的特性和Java字串一至。

連線3個雙引號在Scala中也有特殊意義,它代表被包裹的內容是原始字串,可以不需要字元轉碼。這項特性在定義正規表示式時很有優勢。

還有一個被稱為「字串插值」的字串,他可以直接引用上下文中的變量,並把結果插入字串中。

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 + 1list2: 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

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

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)Stringscala> 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)

除了之前使用Listobject来定义一个列表,还可以使用如下方式:

scala> val list = 1 :: 2 :: 3 :: 4 :: 5 ::
 Nillist: List[Int] = List(1, 2, 3, 4, 5)

List采用前缀操作的方式(所有操作都在列表顶端(开头))进行,::操作符的作用是将一个元素和列表连接起来,并把元素放在列表的开头。这样List的操作就可以定义成一个递归操作。添加一个元素就是把元素加到列表的开头,List只需要更改下头指针,而删除一个元素就是把List的头指针指向列表中的第2个元素。这样,List的实现就非常的高效,它也不需要对内存做任何的转移操作。List有很多常用的方法:

scala> list.indexOf(3)
res6: Int = 2scala> 0 :: listres8: 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> listres12: 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._2res17: Int = 2scala> 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中变量默认都是不可变的),推荐还是采用不可变集合。因为它更直观、线程安全,你可以确定你的变量不会在其它地方被不小心的更改。)

Class

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 = 姓名:杨景, 年龄: 30scala> println(me)
姓名:杨景, 年龄: 30scala> me.name
<console>:20: error: value name is not a member of Person
       me.name
          ^

scala> me.ageres11: Int = 30

case class(样本类)

case class是Scala中学用的一个特性,像Kotlin这样的语言也学习并引入了类似特性(在Kotlin中叫做:data class)。case class具有如下特性:

不需要使用new关键词创建,直接使用类名即可

默认变量都是public final的,不可变的。当然也可以显示指定var、private等特性,但一般不推荐这样用

自动实现了:equals、hashcode、toString等函数

自动实现了:Serializable接口,默认是可序列化的

可应用到match case(模式匹配)中

自带一个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)

object

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(&#39;\n&#39;)
      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 console>

scala> :type allLineString

在readAll函数的定义处,有两个特别的地方:

这个函数定义在了其它函数代码体内部

它有一个@tailrec注解

在Scala中,因为函数是第一类的,它可以被赋值给一个变量。所以Scala中的def定义函数可以等价val func = (x: Int, y: Int) => x + y这个的函数字面量定义函数形式。所以,既然通过变量定义的函数可以放在其它函数代码体内,通过def定义的函数也一样可以放在其它代码体内,这和Javascript很像。

@tailrec注解的含义是这个函数是尾递归函数,编译器在编译时将对其优化成相应的while循环。若一个函数不是尾递归的,加上此注解在编译时将报错。

模式匹配(match case)

模式匹配是函数式编程里面很强大的一个特性。

之前已经见识过了模式匹配的简单使用方式,可以用它替代:if else、switch这样的分支判断。除了这些简单的功能,模式匹配还有一系列强大、易用的特性。

match 中的值、变量和类型

scala> for {
     |   x <- Seq(1, false, 2.7, "one", &#39;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: &#39;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对象提供了几个方法通过回调将执行的结果返还给调用者,常用的有:

onComplete: PartialFunction[Try[T], Unit]:当任务执行完成后调用,无论成功还是失败

onSuccess: PartialFunction[T, Unit]:当任务成功执行完成后调用

onFailure: PartialFunction[Throwable, Unit]:当任务执行失败(异常)时调用

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有了一个基础的认识,并可以写一些简单的代码了。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
在Java應用程序中緩解平台特定問題的策略是什麼?在Java應用程序中緩解平台特定問題的策略是什麼?May 01, 2025 am 12:20 AM

Java如何緩解平台特定的問題? Java通過JVM和標準庫來實現平台無關性。 1)使用字節碼和JVM抽像操作系統差異;2)標準庫提供跨平台API,如Paths類處理文件路徑,Charset類處理字符編碼;3)實際項目中使用配置文件和多平台測試來優化和調試。

Java的平台獨立性與微服務體系結構之間有什麼關係?Java的平台獨立性與微服務體系結構之間有什麼關係?May 01, 2025 am 12:16 AM

java'splatformentenceenhancesenhancesmicroservicesharchitecture byferingDeploymentFlexible,一致性,可伸縮性和便攜性。 1)DeploymentFlexibilityAllowsibilityAllowsOllowsOllowSorlowsOllowsOllowsOllowSeStorunonAnyPlatformwithajvM.2)penterencyCrossServAccAcrossServAcrossServiCessImplifififiesDeevelopmentandeDe

GRAALVM與Java的平台獨立目標有何關係?GRAALVM與Java的平台獨立目標有何關係?May 01, 2025 am 12:14 AM

GraalVM通過三種方式增強了Java的平台獨立性:1.跨語言互操作,允許Java與其他語言無縫互操作;2.獨立的運行時環境,通過GraalVMNativeImage將Java程序編譯成本地可執行文件;3.性能優化,Graal編譯器生成高效的機器碼,提升Java程序的性能和一致性。

您如何測試Java應用程序的平台兼容性?您如何測試Java應用程序的平台兼容性?May 01, 2025 am 12:09 AM

效率testjavaapplicationsforplatformcompatibility oftheSesteps:1)setUpautomatedTestingTestingActingAcrossMultPlatFormSusingCitoolSlikeSlikeJenkinSorgithUbactions.2)contuctualtemualtemalualTesteTESTENRETESTINGINREALHARTWARETOLEALHARDOELHARDOLEATOCATCHISSUSESUSEUSENINCIENVIRENTMENTS.3)schictcross.3)schoscross.3)

Java編譯器(Javac)在實現平台獨立性中的作用是什麼?Java編譯器(Javac)在實現平台獨立性中的作用是什麼?May 01, 2025 am 12:06 AM

Java編譯器通過將源代碼轉換為平台無關的字節碼,實現了Java的平台獨立性,使得Java程序可以在任何安裝了JVM的操作系統上運行。

在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?在平台獨立性的平台獨立性上使用字節碼優於本機代碼的優點是什麼?Apr 30, 2025 am 12:24 AM

ByteCodeachievesPlatFormIndenceByByByByByByExecutedBoviratualMachine(VM),允許CodetorunonanyplatformwithTheApprepreprepvm.Forexample,Javabytecodecodecodecodecanrunonanydevicewithajvm

Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Java真的100%獨立於平台嗎?為什麼或為什麼不呢?Apr 30, 2025 am 12:18 AM

Java不能做到100%的平台獨立性,但其平台獨立性通過JVM和字節碼實現,確保代碼在不同平台上運行。具體實現包括:1.編譯成字節碼;2.JVM的解釋執行;3.標準庫的一致性。然而,JVM實現差異、操作系統和硬件差異以及第三方庫的兼容性可能影響其平台獨立性。

Java的平台獨立性如何支持代碼可維護性?Java的平台獨立性如何支持代碼可維護性?Apr 30, 2025 am 12:15 AM

Java通過“一次編寫,到處運行”實現平台獨立性,提升代碼可維護性:1.代碼重用性高,減少重複開發;2.維護成本低,只需一處修改;3.團隊協作效率高,方便知識共享。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具