Scala入門


保留字

保留字 說明
abstract 抽象聲明
case match表達式中的case子句;定義一個case類
catch 捕捉拋出的異常
class 聲明一個類
def 定義一個方法
do 用於do...while循環
else 與if配對的else語句
extends 表示接下來的class或trait是所聲明的class或trait的父類型
false Boolean的false值
final 用於class或trait,表示不能派生子類型;用於類型成員,則表示派生的class或trait不能覆寫它
for for循環
forSome 用在已存在的類型聲明中,限制其能夠使用的具體類型
if if語句
implicit 使得方法或變量值可以被用於隱含轉換;將方法參數標記為可選的,只要在調用該方法時,作用域內有類型匹配的候選對象,就會使用該對象作為參數
import 將一個或多個類型抑或類型的成員導入到當前作用域
lazy 推遲val變量的賦值
match 用於類型匹配語句
new 創建類的一個實例
null 尚未被賦值的引用變量的值
object 用於單例聲明,單例是只用一個實例的類
override 當原始成員未被聲明為final時,用override覆寫類型中的一個具體成員
package 聲明包的作用域
private 限制某個聲明的可見性
protected 限制某個聲明的可見性
requires 停用,以前用於子類型
return 從函數返回
sealed 用於父類型,要求所有派生的子類型必須在同一個源文件中聲明
super 類似this,單表示父類型
this 對象指向自身的引用;輔助構造函數的方法名
throw 拋出異常
trait 這是一個混入模塊,對類的實例添加額外的狀態和行為;也可以用於聲明而不實現方法.類似java的interface
try 將可能拋出異常的代碼塊包圍起來
true Boolean的true值
type 聲明類型
val 聲明一個"只讀"變量
var 聲明一個可讀可寫的變量
while 用於while循環
with 表示所聲明的類或實例化的對象包括后面的trait
yield 在for循環中返回元素,這些元素會構成一個序列
_ (下划線) 占位符,使用imort,函數字面量中
: 分隔標識符和類型注解
= 賦值
=> 在函數字面量中分隔參數列表與函數體
<- 在for循環中的生成表達式
<: 在參數化類型和抽象類型聲明中,用於限制允許的類型
<% 在參數化類型和抽象類型的view bound生命中
>: 在參數化類型和抽象類型生命中,用於限制允許的類型
# 在類型注入中使用
@ 注解

Scala不存在breakcontinue關鍵字

分號

  • 分號是表達式之間的間隔
  • 當一行結束時,Scala就認為表達式結束了,除非它可以判斷出該表達式尚未結束

變量聲明

  • Scala允許聲明變量是可變的還是不可變的
  • 聲明不可變變量使用val
  • 聲明可變變量使用var
// 聲明不可變變量,這里只是array不可再更改,但是數組內容可以更改
val array:Array[String] = new Array(5)
// 可變
var price: Double = 1.1

Range

  • 生成從某個起點到某個終點的一個數字序列
  • 支持Int, Long, Float, Double, Char, BigInt, BigDecimal
  • 使用to包括區間上限
  • 使用until不包括區間上限
  • 使用by設置步長
scala> 1 to 5
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)

scala> 1 until 5
res3: scala.collection.immutable.Range = Range(1, 2, 3, 4)
scala> 1 to 10 by 3
res0: scala.collection.immutable.Range = Range(1, 4, 7, 10)

scala> 0.1f to 5.3f by 1.0f
res1: scala.collection.immutable.NumericRange[Float] = NumericRange(0.1, 1.1, 2.1, 3.1, 4.1, 5.1)

scala> 'a' to 'g'
res2: scala.collection.immutable.NumericRange.Inclusive[Char] = NumericRange(a, b, c, d, e, f, g)

scala> BigInt(1) to BigInt(5) by 2
res3: scala.collection.immutable.NumericRange[BigInt] = NumericRange(1, 3, 5)

偏函數

  • 不處理所有可能的輸入,只處理那些能與至少一個case語句匹配的輸入
  • 在偏函數中,只能使用case語句
  • 整個函數必須用花括號包圍
  • 如果偏函數被調用,而函數的輸入卻與所有語句都不匹配,系統就會拋出一個MatchError運行時錯誤
  • 使用isDefineAt方法測試特定輸入是否與偏函數匹配
  • 偏函數鏈式連接: pf1 orElse pf2 orElse pf3 ... 只有所有偏函數都不匹配,才會拋出MatchError

示例

代碼

// file: pf.scala
var pf1: PartialFunction[Any, String] = { case s: String => "YES"}
var pf2: PartialFunction[Any, String] = { case d: Double => "YES"}

val pf = pf1 orElse pf2

def tryPF(x: Any, f: PartialFunction[Any, String]): String =
    try {f(x).toString} catch {case _: MatchError => "ERROR!"}

def d(x: Any, f: PartialFunction[Any,String]) =
    f.isDefinedAt(x).toString

println("      |   pf1 - String |   pf2 - Double  |   pf - All")
println("x     | def?  | pf1(x) | def?   | pf2(x) | def?  |  pf(x)")
println("+" * 50)
List("str", 3.14, 10) foreach {
    x => printf("%-5s | %-5s | %-6s | %-6s | %-6s | %-5s | %-6s\n", x.toString, d(x, pf1), tryPF(x, pf1),
        d(x, pf2), tryPF(x, pf2), d(x, pf), tryPF(x, pf))
}

運行

scala pf.scala

運行結果

      |   pf1 - String |   pf2 - Double  |   pf - All
x     | def?  | pf1(x) | def?   | pf2(x) | def?  |  pf(x)
++++++++++++++++++++++++++++++++++++++++++++++++++
str   | true  | YES    | false  | ERROR! | true  | YES   
3.14  | false | ERROR! | true   | YES    | true  | YES   
10    | false | ERROR! | false  | ERROR! | false | ERROR!

方法聲明

方法默認值和命名參數列表

  • 可以為方法參數提供默認值
  • 調用方法時可以只提供部分參數值,可以只傳入指定參數

示例

代碼

case class Point(x: Double = 0.0, y: Double = 0.0) {
	def shift(deltax: Double = 0.0, deltay: Double = 0.0) = 
    	copy (x + deltax, y + deltay)
}

val p1 = new Point(x = 3.3, y = 4.4)
val p2 = p1.copy(y = 6.6)

方法具有多個參數列表

示例

代碼

abstract class Shape() {
    def draw(offset: Point = Point(0.0, 0.0))(f: String => Unit): Unit =
        f(s"draw(offset = $offset), ${this.toString}")
}

case class Circle(center: Point, radius: Double) extends Shape

調用有多個參數列表的draw方法

s.draw(Point(1.0, 2.0))(str => println(s"ShapesDrawingActor: $str"))
// 也可以將圓括號替換為花括號
s.draw(Point(1.0, 2.0)){str => println(s"ShapesDrawingActor: $str")}
// 或者這樣
s.draw(Point(1.0, 2.0)){str =>
	println(s"ShapesDrawingActor: $str")
}
// 亦或這樣
s.draw(Point(1.0, 2.0)){
	str => println(s"ShapesDrawingActor: $str")
}

優勢

  • 代碼更清晰
  • 在之后的參數列表中進行類型判斷
  • 可以用最后一個參數列表來推斷隱含參數.隱含參數是用implicit關鍵字聲明的參數.

Future簡介

  • scala.concurrent.Future是Scala提供的一個並發工具,其中的API使用隱含參數來減少冗余代碼
  • 將任務封裝在Future中執行時,該任務的執行是異步的

示例

代碼

並發發出5個任務,並在任務結束時處理任務返回的結果.

// file: future.scala
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def sleep(millis: Long) = {
    Thread.sleep(millis)
}

def doWork(index: Int) = {
    sleep((math.random * 1000).toLong)
    index
}

(1 to 5) foreach { index =>
    val future = Future(doWork(index))

    future onSuccess {
        case answer: Int => println(s"Success! returned: $answer")
    }
    future onFailure {
        case th: Throwable => println(s"Failure! returned: $th")
    }
}

sleep(3000)     // 等待足夠長時間,確保工作進程結束
println("finito!")

運行

scala future.scala

運行結果

Success! returned: 2
Success! returned: 4
Success! returned: 1
Success! returned: 5
Success! returned: 3
finito!

說明

  • 用foreach對一個從1到5的Range進行迭代,調用了scala.concurrent.Futrue.apply, 這是單例對象Future的工廠方法.
  • Future.apply返回一個新的Future對象,然后控制權就交給循環了,該對象將在另一個線程中執行doWork(index)
  • 用onSuccess注冊一個回調函數,當future成功執行完畢后,該回調函數會被執行.這個回調函數是一個偏函數.
  • Future API允許我們通過ExecutionContext來配置並發操作的執行.import scala.concurrent.ExecutionContext.Implicits.global語句導入了默認的ExecutionContext.示例中調用了3個方法,其中的這些方法的第二個參數列表具有隱含的ExecutionContext參數,由於沒有指定,使用了默認的ExecutionContext.
  • 使用implicit關鍵字聲明變量為implicit.只有被聲明為implicit的函數參數才允許調用時不給出實參.

嵌套方法的定義和遞歸

方法的定義可以嵌套.

代碼示例

代碼

// file: factorial.scala
def factorial(i: Int): Long = {
    def fact(i: Int, accumulator: Int): Long = {
        if (i <= 1)
            accumulator
        else
            fact(i - 1, i * accumulator)
    }

    fact(i, 1)
}

(0 to 5) foreach ( i => println(factorial(i)) )

運行

scala factorial.scala

輸出

1
1
2
6
24
120

字面量

整數字面量

  • 整數字面量可以以十進制,八進制,十六進制的形式出現
類型 格式 例子
十進制 0或非零值,后面跟上0個活多個數字 0,1,321
十六進制 0x后面更上一個或多個十六進制數字(0-9,A-F,a-f) oxFF,0x1a3b
八進制 0后面跟上一個或多個八進制數字(0-7) 013,077

整數字面量

目標類型 下限 上限
Long -2^63 2^63-1
Int -2^31 2^31-1
Short -2^15 2^15-1
Char 0 2^16
Byte -2^7 2^7-1
  • 如果整數字面量的值超出了以上表格中所示的范圍,將會引發一個編譯錯誤.
  • 字面量類型默認推斷為Int

浮點數字面量

  • 默認推斷類型為Double

示例

.14
3.14
3.14F
3.14D
3E5
3.14e-5
3.14e+5
3.14e-4D

布爾型字面量

布爾型字面量可以為truefalse

字符字面量

  • 字符字面量要么是單引號內的一個可打印的Unicode字符,要么是一個轉義序列.
  • 值在0~255的Unicode字符可以用八進制數字的轉義形式表示,即一個反斜杠后面跟上最多三個八進制數字字符

字符常量示例

'A'
'\u0041'
'\n'
'012'
'\t'

字符轉義序列

轉義序列 含義
\b 退格(BS)
\t 水平制表符(HT)
\n 換行(LF)
\f 表格換行(FF)
\r 回車(CR)
" 雙引號(")
' 單引號(')
\ 反斜杠()

不可打印的Unicode字符是不允許的,如\u0009(水平制表符), 應該使用等價的轉義形式\t

字符串字面量

  • 字符串字面量是被雙引號和三重雙引號包圍的字符串序列

符號字面量

  • 符號是一些規定的字符串
  • 符號字面量是單引號后更上一個或多個數字,字母或下划線,但第一個字符不能是數字
  • 符號字面量'id是表達式scala.Symbol("id")的簡寫形式
  • 如果需要創建包含空格的符號,可以使用Symbol.apply, 如Symbol.apply(" Programming Scala ")

函數字面量

  • (i: Int, s: String) => s+i 是一個類型為 Function2[Int, String, String] (返回值類型為String)的函數字面量

  • 我們可以使用函數字面量來聲明變量,下面兩種聲明是等價的:

    val f1: (Int, String) => String 	= (i, s) => s+i
    val f2: Function2[Int, String, String]	= (i, s) => s+i
    

元組字面量

  • Scala庫中包含TupleN類,如Tuple2, 用於組建N元素組

定義

val tup = ("strig", 2016)	// 定義了一個Tuple2的實例
val t1: (Int, String) = (1, "one")
val t2: Tuple2[Int, String] = (2, "two")

使用

// File: tuple.scala
val t = ("Hello", 1, 2.3)
println("print the whole tuple:" + t)
println("print the first item:" + t._1)
println("print the second item:" + t._2)
println("print the third item:" + t._3)

val (t1, t2, t3) = ("World", '!', 0x22)
println(t1 + ", " + t2 + ", " + t3)

val (t4, t5, t6) = Tuple3("World", '!', 0x22)
println(t4 + ", " + t5 + ", " + t6)

運行

scala tuple.scala

輸出

print the whole tuple:(Hello,1,2.3)
print the first item:Hello
print the second item:1
print the third item:2.3
World, !, 34
World, !, 34

兩元素的元組

兩元素的元組有時被簡稱為pair.其定義方法有多種:

  • (1, "one")
  • 1 -> "one"
  • Tuple2(1, "one")

示例:

scala> val t = 1 -> "one"
t: (Int, String) = (1,one)

scala> t._1
res0: Int = 1

scala> t._2
res1: String = one

Option, Some,None: 避免使用null

  • Option有兩個具體的子類,SomeNone.
  • Some表示有值
  • None表示沒有值

封閉類的繼承

  • Scala設計了關鍵字sealed,其告訴編譯器所有的子類必須在同一個源文件中聲明.在Scala庫中,Some與None就是與Option聲明在同一源文件中,這一技術有效防止了Option類派生其他子類型.
  • 如果要防止用戶派生任何子類, 可以用final關鍵字聲明

用文件和名空間組織代碼

  • Scala沿用Java用包來表示命名空間的這一做法,但它更加靈活.
  • 文件名不必與類名一致
  • 包結構不一定要與目錄結構一致
  • 可以定義與文件的"物理"位置獨立的包結構
  • Scala不允許在腳本中定義包,腳本被隱含包裝在一個對象中.在對象中聲明包是不允許的
  • 不能再類或對象中定義包

導入類型及其成員

  • _被當做通配符, 因為在Scala中,*允許用作函數名

示例

Scala中導入Java類型.

import java.awt._				// 導入包內所有類型
import java.io.File				// 導入包中單獨的Scala類型或Java類型
import java.io.File._			// 導入了java.io.File中所有的靜態方法和屬性.與之等價的Java import語句為:
							  // import static java.io.File.*
import java.util.{Map, HashMap}	// 選擇性導入
  • 導入是相對的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM