
- Scala 教程
- Scala - 首頁
- Scala - 概述
- Scala - 特性
- Scala - 環境搭建
- Scala - 構建工具 (SBT)
- Scala - 基本語法
- 資料型別和變數
- Scala - 資料型別
- Scala - 變數
- Scala - 字串
- Scala - 陣列
- Scala 運算子
- Scala - 運算子
- Scala - 算術運算子
- Scala - 關係運算符
- Scala - 邏輯運算子
- Scala - 位運算子
- Scala - 賦值運算子
- Scala 條件語句
- Scala - if else
- Scala 迴圈語句
- Scala - 迴圈語句
- Scala - while 迴圈
- Scala - do-while 迴圈
- Scala - for 迴圈
- Scala - break 語句
- Scala 類和物件
- Scala - 類和物件
- Scala - 訪問修飾符
- Scala 方法和函式
- Scala - 函式
- Scala - 按名稱呼叫函式
- Scala - 帶命名引數的函式
- Scala - 帶變長引數的函式
- Scala - 遞迴函式
- Scala - 預設引數值
- Scala - 高階函式
- Scala - 巢狀函式
- Scala - 匿名函式
- 部分應用函式
- Scala - 柯里化函式
- Scala 集合
- Scala - 集合
- Scala - 列表
- Scala - 集合
- Scala - 對映
- Scala - 元組
- Scala - 迭代器
- Scala - Options
- Scala 模式匹配
- Scala - 模式匹配
- Scala - 異常處理
- Scala - 提取器
- Scala - 正則表示式
- Scala 檔案 I/O
- Scala - 檔案 I/O
- Scala 高階概念
- Scala - 閉包
- Scala - 特質
- Scala 有用資源
- Scala 快速指南
- Scala - 線上編譯器
- Scala - 有用資源
- Scala - 討論
Scala 快速指南
Scala - 概述
Scala,縮寫為 Scalable Language,是一種混合函數語言程式設計語言。它由 Martin Odersky 建立。Scala 平滑地整合了面向物件和函式式語言的特性。Scala 編譯後可在 Java 虛擬機器上執行。許多依賴 Java 進行業務關鍵應用程式的現有公司正在轉向 Scala 以提高其開發效率、應用程式可擴充套件性和整體可靠性。
這裡我們列舉了一些使 Scala 成為應用程式開發人員首選的原因。
Scala 是面向物件的
Scala 是一種純面向物件的語言,因為每個值都是一個物件。物件的型別和行為由類和特質描述,這些將在後續章節中解釋。
類透過子類化擴充套件,並使用靈活的基於混入的組合機制作為多重繼承的簡潔替代。
Scala 是函式式的
Scala 也是一種函式式語言,因為每個函式都是一個值,每個值都是一個物件,所以最終每個函式都是一個物件。
Scala 提供了輕量級的語法來定義匿名函式,它支援高階函式,允許函式巢狀,並支援柯里化。這些概念將在後續章節中解釋。
Scala 是靜態型別的
與其他一些靜態型別語言(C、Pascal、Rust 等)不同,Scala 不需要您提供冗餘的型別資訊。在大多數情況下,您不必指定型別,當然也不必重複。
Scala 執行在 JVM 上
Scala 編譯成 Java 位元組碼,由 Java 虛擬機器 (JVM) 執行。這意味著 Scala 和 Java 有一個共同的執行時平臺。您可以輕鬆地從 Java 遷移到 Scala。
Scala 編譯器將您的 Scala 程式碼編譯成 Java 位元組碼,然後可以透過“scala”命令執行。 “scala”命令類似於java命令,它執行您編譯後的 Scala 程式碼。
Scala 可以執行 Java 程式碼
Scala 使您可以使用所有 Java SDK 類以及您自己的自定義 Java 類或您最喜歡的 Java 開源專案。
Scala 可以進行併發和同步處理
Scala 允許您以有效的方式表達一般的程式設計模式。它減少了程式碼行數,並幫助程式設計師以型別安全的方式編寫程式碼。它允許您以不可變的方式編寫程式碼,這使得應用併發和並行(同步)變得容易。
Scala vs Java
Scala 擁有一套與 Java 完全不同的特性。其中一些是:
- 所有型別都是物件
- 型別推斷
- 巢狀函式
- 函式是物件
- 領域特定語言 (DSL) 支援
- 特質
- 閉包
- 受 Erlang 啟發的併發支援
Scala Web 框架
Scala 被廣泛應用,尤其是在企業 Web 應用程式中。您可以檢視一些最流行的 Scala Web 框架:
Scala - 環境搭建
Scala 可以安裝在任何類 UNIX 或基於 Windows 的系統上。在開始在您的機器上安裝 Scala 之前,您必須在您的計算機上安裝 Java 1.8 或更高版本。
按照以下步驟安裝 Scala。
步驟 1:驗證您的 Java 安裝
首先,您需要在您的系統上安裝 Java 軟體開發工具包 (SDK)。要驗證這一點,請根據您使用的平臺執行以下兩個命令中的任何一個。
如果 Java 安裝已正確完成,則它將顯示您 Java 安裝的當前版本和規範。示例輸出如下表所示。
平臺 | 命令 | 示例輸出 |
---|---|---|
Windows |
開啟命令控制檯並鍵入: >java –version |
Java 版本 "1.8.0_31" Java(TM) SE 執行時環境 (build 1.8.0_31-b31) Java HotSpot(TM) 64位伺服器虛擬機器 (build 25.31-b07, mixed mode) |
Linux |
開啟命令終端並鍵入: $java –version |
Java 版本 "1.8.0_31" OpenJDK 執行時環境 (rhel-2.8.10.4.el6_4-x86_64) OpenJDK 64位伺服器虛擬機器 (build 25.31-b07, mixed mode) |
我們假設本教程的讀者在他們的系統上安裝了 Java SDK 版本 1.8.0_31。
如果您沒有 Java SDK,請從http://www.oracle.com/technetwork/java/javase/downloads/index.html下載其當前版本並安裝它。
步驟 2:設定您的 Java 環境
設定環境變數 JAVA_HOME 以指向 Java 安裝在您機器上的基目錄位置。例如:
序號 | 平臺和描述 |
---|---|
1 |
Windows 將 JAVA_HOME 設定為 C:\ProgramFiles\java\jdk1.7.0_60 |
2 |
Linux export JAVA_HOME=/usr/local/java-current |
將 Java 編譯器位置的完整路徑新增到系統路徑。
序號 | 平臺和描述 |
---|---|
1 |
Windows 將字串 "C:\Program Files\Java\jdk1.7.0_60\bin" 附加到系統變數 PATH 的末尾。 |
2 |
Linux export PATH=$PATH:$JAVA_HOME/bin/ |
如上所述,從命令提示符執行命令java -version。
步驟 3:安裝 Scala
您可以從http://www.scala-lang.org/downloads下載 Scala。在撰寫本教程時,我下載了“scala-2.11.5-installer.jar”。確保您擁有管理員許可權才能繼續。現在,在命令提示符下執行以下命令:
平臺 | 命令和輸出 | 描述 |
---|---|---|
Windows | >java –jar scala-2.11.5-installer.jar> |
此命令將顯示一個安裝嚮導,該向導將指導您在 Windows 機器上安裝 Scala。在安裝過程中,它將要求您接受許可協議,只需接受它,然後它將詢問將安裝 Scala 的路徑。我選擇了預設路徑“C:\Program Files\Scala”,您可以根據自己的方便選擇合適的路徑。 |
Linux |
命令: $java –jar scala-2.9.0.1-installer.jar 輸出: 歡迎安裝 Scala 2.9.0.1! 按 1 繼續,按 2 退出,按 3 重新顯示 1................................................ [ 開始解包 ] [ 處理包:軟體包安裝 (1/1) ] [ 解包完成 ] [ 控制檯安裝完成 ] |
在安裝過程中,它將要求您接受許可協議,要接受它,請鍵入 1,它將詢問將安裝 Scala 的路徑。我輸入了`/usr/local/share`,您可以根據自己的方便選擇合適的路徑。 |
最後,開啟一個新的命令提示符並鍵入scala -version然後按 Enter 鍵。您應該看到以下內容:
平臺 | 命令 | 輸出 |
---|---|---|
Windows | >scala -version |
Scala 程式碼執行器版本 2.11.5 -- 版權所有 2002-2013,LAMP/EPFL |
Linux | $scala -version |
Scala 程式碼執行器版本 2.9.0.1 – 版權所有 2002-2013,LAMP/EPFL |
Scala - 基本語法
如果您對 Java 有很好的理解,那麼學習 Scala 將非常容易。Scala 和 Java 之間最大的語法差異是分號 ';' 行尾字元是可選的。
當我們考慮一個 Scala 程式時,它可以定義為透過呼叫彼此方法進行通訊的物件集合。現在讓我們簡要了解一下類、物件、方法和例項變數分別是什麼意思。
物件 - 物件具有狀態和行為。物件是類的例項。例如 - 狗具有狀態 - 顏色、名稱、品種以及行為 - 搖尾巴、吠叫和吃東西。
類 - 類可以定義為描述與類相關的行為/狀態的模板/藍圖。
方法 - 方法基本上是一種行為。一個類可以包含許多方法。邏輯是在方法中編寫的,資料被操作,所有操作都被執行。
欄位 - 每個物件都有一組唯一的例項變數,稱為欄位。物件的 state 是由分配給這些欄位的值建立的。
閉包 - 閉包是一個函式,其返回值取決於在此函式外部宣告的一個或多個變數的值。
特質 - 特質封裝了方法和欄位定義,然後可以透過將它們混入類中來重複使用它們。特質用於透過指定支援方法的簽名來定義物件型別。
第一個 Scala 程式
我們可以透過兩種模式執行 Scala 程式:一種是互動模式,另一種是指令碼模式。
互動模式
開啟命令提示符並使用以下命令開啟 Scala。
\>scala
如果您的系統中安裝了 Scala,則將顯示以下輸出:
Welcome to Scala version 2.9.0.1 Type in expressions to have them evaluated. Type :help for more information.
在 Scala 提示符的右側鍵入以下文字,然後按 Enter 鍵:
scala> println("Hello, Scala!");
它將產生以下結果:
Hello, Scala!
指令碼模式
使用以下說明以指令碼模式編寫 Scala 程式。開啟記事本並將以下程式碼新增到其中。
示例
object HelloWorld { /* This is my first java program. * This will print 'Hello World' as the output */ def main(args: Array[String]) { println("Hello, world!") // prints Hello World } }
將檔案儲存為 − HelloWorld.scala。
開啟命令提示符視窗並轉到儲存程式檔案的目錄。使用“scalac”命令編譯 Scala 程式,它將在當前目錄中生成一些類檔案。其中一個將被稱為HelloWorld.class。這是一個位元組碼,它將使用“scala”命令在 Java 虛擬機器 (JVM) 上執行。
使用以下命令編譯和執行您的 Scala 程式。
\> scalac HelloWorld.scala \> scala HelloWorld
輸出
Hello, World!
基本語法
以下是 Scala 程式設計中的基本語法和編碼約定。
大小寫敏感性 − Scala 區分大小寫,這意味著識別符號Hello和hello在 Scala 中具有不同的含義。
類名 − 所有類名的第一個字母都應大寫。如果使用多個單詞來構成類名,則每個內部單詞的第一個字母都應大寫。
示例 − class MyFirstScalaClass。
方法名 − 所有方法名都應以小寫字母開頭。如果使用多個單詞來構成方法名,則每個內部單詞的第一個字母都應大寫。
示例 − def myMethodName()
程式檔名 − 程式檔名應與物件名完全匹配。儲存檔案時,應使用物件名儲存它(記住 Scala 區分大小寫),並在名稱末尾附加“.scala”。(如果檔名和物件名不匹配,則程式將無法編譯)。
示例 − 假設“HelloWorld”是物件名。則檔案應儲存為“HelloWorld.scala”。
def main(args: Array[String]) − Scala 程式處理從 main() 方法開始,它是每個 Scala 程式的必備部分。
Scala 識別符號
所有 Scala 元件都需要名稱。用於物件、類、變數和方法的名稱稱為識別符號。關鍵字不能用作識別符號,並且識別符號區分大小寫。Scala 支援四種類型的識別符號。
字母數字識別符號
字母數字識別符號以字母或下劃線開頭,後面可以跟字母、數字或下劃線。'$' 字元是 Scala 中的保留關鍵字,不應在識別符號中使用。
以下是合法的字母數字識別符號 −
age, salary, _value, __1_value
以下是非法的識別符號 −
$salary, 123abc, -salary
運算子識別符號
運算子識別符號由一個或多個運算子字元組成。運算子字元是可列印的 ASCII 字元,例如 +、:、?、~ 或 #。
以下是合法的運算子識別符號 −
+ ++ ::: <?> :>
Scala 編譯器將在內部“修改”運算子識別符號,將其轉換為包含嵌入式 $ 字元的合法 Java 識別符號。例如,識別符號 :-> 在內部將表示為 $colon$minus$greater。
混合識別符號
混合識別符號由一個字母數字識別符號組成,後面跟一個下劃線和一個運算子識別符號。
以下是合法的混合識別符號 −
unary_+, myvar_=
這裡,unary_+ 用作方法名定義一元 + 運算子,myvar_= 用作方法名定義賦值運算子(運算子過載)。
字面量識別符號
字面量識別符號是用反引號 (`. . . ` )括起來的任意字串。
以下是合法的字面量識別符號 −
`x` `<clinit>` `yield`
Scala 關鍵字
以下列表顯示了 Scala 中的保留字。這些保留字不能用作常量或變數或任何其他識別符號名稱。
abstract | case | catch | class |
def | do | else | extends |
false | final | finally | for |
forSome | if | implicit | import |
lazy | match | new | Null |
object | override | package | private |
protected | return | sealed | super |
this | throw | trait | Try |
true | type | val | var |
while | with | yield | |
- | : | = | => |
<- | <: | <% | >: |
# | @ |
Scala 中的註釋
Scala 支援單行和多行註釋,與 Java 非常相似。多行註釋可以巢狀,但必須正確巢狀。Scala 編譯器會忽略任何註釋中所有可用的字元。
object HelloWorld { /* This is my first java program. * This will print 'Hello World' as the output * This is an example of multi-line comments. */ def main(args: Array[String]) { // Prints Hello World // This is also an example of single line comment. println("Hello, world!") } }
空行和空格
僅包含空格(可能帶有註釋)的行稱為空行,Scala 會完全忽略它。標記可以用空格字元和/或註釋分隔。
換行符
Scala 是一種面向行的語言,其中語句可以用分號 (;) 或換行符終止。語句末尾的分號通常是可選的。如果需要,可以輸入一個,但如果語句單獨出現在一行中,則不必輸入。另一方面,如果在一行中編寫多個語句,則需要分號。以下語法是多個語句的用法。
val s = "hello"; println(s)
Scala 包
包是程式碼的命名模組。例如,Lift 實用程式包是 net.liftweb.util。包宣告是原始檔中的第一行非註釋行,如下所示 −
package com.liftcode.stuff
可以匯入 Scala 包,以便可以在當前編譯範圍內引用它們。以下語句匯入 scala.xml 包的內容 −
import scala.xml._
可以匯入單個類和物件,例如,來自 scala.collection.mutable 包的 HashMap −
import scala.collection.mutable.HashMap
可以從單個包中匯入多個類或物件,例如,來自 scala.collection.immutable 包的 TreeMap 和 TreeSet −
import scala.collection.immutable.{TreeMap, TreeSet}
應用動態
啟用動態呼叫的標記特徵。此特徵的例項 x 允許對任意方法名 meth 和引數列表 args 進行方法呼叫 x.meth(args),以及對任意欄位名 field 進行欄位訪問 x.field。此功能在 Scala-2.10 中引入。
如果 x 本身不支援呼叫(即,如果型別檢查失敗),則會根據以下規則重寫它 −
foo.method("blah") ~~> foo.applyDynamic("method")("blah") foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah")) foo.method(x = 1, 2) ~~> foo.applyDynamicNamed("method")(("x", 1), ("", 2)) foo.field ~~> foo.selectDynamic("field") foo.varia = 10 ~~> foo.updateDynamic("varia")(10) foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13) foo.arr(10) ~~> foo.applyDynamic("arr")(10)
Scala - 資料型別
Scala 與 Java 具有相同的全部資料型別,具有相同的記憶體佔用量和精度。以下是表格,其中詳細介紹了 Scala 中可用的所有資料型別 −
序號 | 資料型別和描述 |
---|---|
1 |
Byte 8 位有符號值。範圍從 -128 到 127 |
2 |
Short 16 位有符號值。範圍 -32768 到 32767 |
3 |
Int 32 位有符號值。範圍 -2147483648 到 2147483647 |
4 |
Long 64 位有符號值。-9223372036854775808 到 9223372036854775807 |
5 |
Float 32 位 IEEE 754 單精度浮點數 |
6 |
Double 64 位 IEEE 754 雙精度浮點數 |
7 |
Char 16 位無符號 Unicode 字元。範圍從 U+0000 到 U+FFFF |
8 |
String 一系列 Char |
9 |
Boolean 文字 true 或文字 false |
10 |
Unit 對應於無值 |
11 |
Null null 或空引用 |
12 |
Nothing 每種其他型別的子型別;不包含任何值 |
13 |
Any 任何型別的超型別;任何物件都是 Any 型別 |
14 |
AnyRef 任何引用型別的超型別 |
上面列出的所有資料型別都是物件。與 Java 中不同,沒有基本型別。這意味著可以在 Int、Long 等上呼叫方法。
Scala 基本字面量
Scala 用於字面量的規則簡單直觀。本節解釋所有基本的 Scala 字面量。
整數字面量
整數字面量通常為 Int 型別,或者在後跟 L 或 l 字尾時為 Long 型別。以下是一些整數字面量 −
0 035 21 0xFFFFFFFF 0777L
浮點數字面量
浮點數字面量在後跟浮點數型別字尾 F 或 f 時為 Float 型別,否則為 Double 型別。以下是一些浮點數字面量 −
0.0 1e30f 3.14159f 1.0e100 .1
布林字面量
布林字面量true和false是 Boolean 型別的成員。
符號字面量
符號字面量 'x' 是表示式scala.Symbol("x")的簡寫。Symbol 是一個 case class,定義如下。
package scala final case class Symbol private (name: String) { override def toString: String = "'" + name }
字元字面量
字元字面量是用引號括起來的單個字元。該字元或者是可列印的 Unicode 字元,或者是用轉義序列描述的。以下是一些字元字面量 −
'a' '\u0041' '\n' '\t'
字串字面量
字串字面量是用雙引號括起來的一系列字元。這些字元或者是可列印的 Unicode 字元,或者是用轉義序列描述的。以下是一些字串字面量 −
"Hello,\nWorld!" "This string contains a \" character."
多行字串
多行字串字面量是用三個引號 """ ... """括起來的一系列字元。字元序列是任意的,除非它可能只在最後包含三個或更多連續的引號字元。
字元不一定是可列印的;換行符或其他控制字元也是允許的。這是一個多行字串字面量 −
"""the present string spans three lines."""
空值
空值為scala.Null型別,因此與每個引用型別相容。它表示引用一個特殊的“null”物件的引用值。
轉義序列
在字元和字串字面量中識別以下轉義序列。
轉義序列 | Unicode | 描述 |
---|---|---|
\b | \u0008 | 退格 BS |
\t | \u0009 | 水平製表符 HT |
\n | \u000c | 換頁 FF |
\f | \u000c | 換頁 FF |
\r | \u000d | 回車 CR |
\" | \u0022 | 雙引號 " |
\' | \u0027 | 單引號 . |
\\ | \u005c | 反斜槓 \ |
Unicode 在 0 到 255 之間的字元也可以用八進位制轉義表示,即反斜槓 '\' 後跟最多三個八進位制字元的序列。以下示例顯示了一些轉義序列字元 −
示例
object Test { def main(args: Array[String]) { println("Hello\tWorld\n\n" ); } }
編譯並執行上述程式碼後,將產生以下結果 −
輸出
Hello World
Scala - 變數
變數只不過是保留的記憶體位置以儲存值。這意味著當建立變數時,會在記憶體中保留一些空間。
根據變數的資料型別,編譯器會分配記憶體並決定可以在保留的記憶體中儲存什麼。因此,透過為變數分配不同的資料型別,可以在這些變數中儲存整數、小數或字元。
變數宣告
Scala 具有不同的變數宣告語法。它們可以定義為值,即常量或變數。這裡,myVar 使用關鍵字 var 宣告。它是一個可以改變值的變數,這稱為可變變數。以下是使用var關鍵字定義變數的語法 −
語法
var myVar : String = "Foo"
這裡,myVal 使用關鍵字 val 宣告。這意味著它是一個不能改變值的變數,這稱為不可變變數。以下是使用 val 關鍵字定義變數的語法 −
語法
val myVal : String = "Foo"
變數資料型別
變數的型別在變數名之後和等號之前指定。可以透過提及其資料型別來定義任何型別的 Scala 變數,如下所示 −
語法
val or val VariableName : DataType = [Initial Value]
如果不為變數分配任何初始值,則它是有效的,如下所示 −
語法
var myVar :Int; val myVal :String;
變數型別推斷
為變數分配初始值時,Scala 編譯器可以根據分配給它的值推斷出變數的型別。這稱為變數型別推斷。因此,可以這樣編寫這些變數宣告 −
語法
var myVar = 10; val myVal = "Hello, Scala!";
這裡,預設情況下,myVar 將為 Int 型別,myVal 將成為 String 型別變數。
多重賦值
Scala 支援多重賦值。如果程式碼塊或方法返回一個元組(元組 − 保持不同型別的物件的集合),則元組可以賦值給 val 變數。[注意 − 我們將在後續章節中學習元組。]
語法
val (myVar1: Int, myVar2: String) = Pair(40, "Foo")
並且型別推斷能夠正確處理 −
語法
val (myVar1, myVar2) = Pair(40, "Foo")
示例程式
以下是一個示例程式,它解釋了在 Scala 中宣告變數的過程。此程式宣告四個變數 — 兩個變數是用型別宣告定義的,其餘兩個沒有型別宣告。
示例
object Demo { def main(args: Array[String]) { var myVar :Int = 10; val myVal :String = "Hello Scala with datatype declaration."; var myVar1 = 20; val myVal1 = "Hello Scala new without datatype declaration."; println(myVar); println(myVal); println(myVar1); println(myVal1); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
10 Hello Scala with datatype declaration. 20 Hello Scala without datatype declaration.
變數作用域
Scala 中的變數根據其使用位置可以有三種不同的作用域。它們可以作為欄位、方法引數和區域性變數存在。以下是關於每種作用域型別的詳細資訊。
欄位
欄位是屬於物件的變數。欄位可以從物件的每個方法內部訪問。欄位也可以從物件外部訪問,具體取決於宣告欄位時使用的訪問修飾符。物件欄位可以是可變型別和不可變型別,並且可以使用var或val定義。
方法引數
方法引數是變數,用於在呼叫方法時將值傳遞到方法內部。方法引數只能從方法內部訪問,但是如果從方法外部持有物件的引用,則傳遞的物件可能可以從外部訪問。方法引數始終是不可變的,由val關鍵字定義。
區域性變數
區域性變數是在方法內部宣告的變數。區域性變數只能從方法內部訪問,但是如果從方法中返回建立的物件,則這些物件可能會“逃逸”方法。區域性變數可以是可變型別和不可變型別,並且可以使用var或val定義。
Scala - 類和物件
本章將引導您學習如何在 Scala 程式設計中使用類和物件。類是物件的藍圖。定義類後,可以使用關鍵字new從類藍圖建立物件。透過物件,您可以使用已定義類所有功能。
下圖以“學生”類為例演示了類和物件,該類包含成員變數(姓名和學號)和成員方法(setName() 和 setRollNo())。最終,所有這些都是類的成員。類是藍圖,物件是實際存在的實體。在下圖中,“學生”是一個類,Harini、John 和 Maria 是“學生”類的物件,它們都具有姓名和學號。

基本類
以下是定義 Scala 基本類的簡單語法。此類定義了兩個變數x和y以及一個方法:move,該方法不返回值。類變數稱為類的欄位,方法稱為類方法。
類名充當類建構函式,它可以接受多個引數。上面的程式碼定義了兩個建構函式引數xc和yc;它們在整個類的主體中都是可見的。
語法
class Point(xc: Int, yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy println ("Point x location : " + x); println ("Point y location : " + y); } }
如本章前面所述,您可以使用關鍵字new建立物件,然後可以訪問類欄位和方法,如下面的示例所示:
示例
import java.io._ class Point(val xc: Int, val yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy println ("Point x location : " + x); println ("Point y location : " + y); } } object Demo { def main(args: Array[String]) { val pt = new Point(10, 20); // Move to a new location pt.move(10, 10); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Point x location : 20 Point y location : 30
擴充套件類
您可以擴充套件基本 Scala 類,並且可以像在 Java 中一樣設計繼承類(使用extends關鍵字),但是有兩個限制:方法重寫需要override關鍵字,並且只有主建構函式才能將引數傳遞給基建構函式。讓我們擴充套件上面的類並新增另一個類方法。
示例
讓我們以兩個類為例:Point 類(與上面的示例相同)和 Location 類,Location 類使用 extends 關鍵字繼承自 Point 類。“extends”子句有兩個作用:它使 Location 類繼承 Point 類所有非私有成員,並使型別Location成為型別Point類的子型別。因此,這裡 Point 類稱為超類,類Location稱為子類。擴充套件類並繼承父類所有特性稱為繼承,但是 Scala 只允許從一個類繼承。
注意 - Point 類中的 move() 方法和Location 類中的 move() 方法並沒有重寫 move 的相應定義,因為它們是不同的定義(例如,前者接受兩個引數,而後者接受三個引數)。
嘗試以下示例程式來實現繼承。
import java.io._ class Point(val xc: Int, val yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy println ("Point x location : " + x); println ("Point y location : " + y); } } class Location(override val xc: Int, override val yc: Int, val zc :Int) extends Point(xc, yc){ var z: Int = zc def move(dx: Int, dy: Int, dz: Int) { x = x + dx y = y + dy z = z + dz println ("Point x location : " + x); println ("Point y location : " + y); println ("Point z location : " + z); } } object Demo { def main(args: Array[String]) { val loc = new Location(10, 20, 15); // Move to a new location loc.move(10, 10, 5); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Point x location : 20 Point y location : 30 Point z location : 20
隱式類
隱式類允許在類在作用域內時與類的 primary 建構函式進行隱式轉換。隱式類是用“implicit”關鍵字標記的類。此功能在 Scala 2.10 中引入。
語法 - 以下是隱式類的語法。這裡的隱式類始終在物件作用域中,其中允許所有方法定義,因為隱式類不能是頂級類。
語法
object <object name> { implicit class <class name>(<Variable>: Data type) { def <method>(): Unit = } }
示例
讓我們以一個名為IntTimes的隱式類為例,它包含 times() 方法。這意味著 times() 包含一個迴圈事務,該事務將執行給定語句指定的次數。假設給定語句是“4 times println (“Hello”)”,這意味著 println (“Hello”) 語句將執行 4 次。
以下是給定示例的程式。在此示例中使用了兩個物件類(Run 和 Demo),因此必須將這兩個類分別儲存到不同的檔案中,檔名分別為 Run 和 Demo,如下所示。
Run.scala - 將以下程式儲存到 Run.scala 中。
object Run { implicit class IntTimes(x: Int) { def times [A](f: =>A): Unit = { def loop(current: Int): Unit = if(current > 0){ f loop(current - 1) } loop(x) } } }
Demo.scala - 將以下程式儲存到 Demo.scala 中。
import Run._ object Demo { def main(args: Array[String]) { 4 times println("hello") } }
以下命令用於編譯和執行這兩個程式。
命令
\>scalac Run.scala \>scalac Demo.scala \>scala Demo
輸出
Hello Hello Hello Hello
注意 -
隱式類必須定義在另一個類/物件/特質內部(不能在頂級)。
隱式類在其建構函式中只能接受一個非隱式引數。
隱式類不能是作用域內任何名稱與隱式類相同的任何方法、成員或物件。
單例物件
Scala 比 Java 更面向物件,因為在 Scala 中,我們不能有靜態成員。相反,Scala 有單例物件。單例是一個只能有一個例項的類,即物件。您可以使用關鍵字object而不是 class 關鍵字來建立單例。由於您無法例項化單例物件,因此您無法將引數傳遞給主建構函式。您已經看到所有使用單例物件的示例,在這些示例中,您呼叫了 Scala 的 main 方法。
以下是實現單例的相同示例程式。
示例
import java.io._ class Point(val xc: Int, val yc: Int) { var x: Int = xc var y: Int = yc def move(dx: Int, dy: Int) { x = x + dx y = y + dy } } object Demo { def main(args: Array[String]) { val point = new Point(10, 20) printPoint def printPoint{ println ("Point x location : " + point.x); println ("Point y location : " + point.y); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Point x location : 10 Point y location : 20
Scala - 訪問修飾符
本章將介紹 Scala 訪問修飾符。包、類或物件的成員可以使用訪問修飾符 private 和 protected 進行標記,如果我們不使用這兩個關鍵字中的任何一個,則訪問將被假定為 public。這些修飾符將對成員的訪問限制在程式碼的某些區域。要使用訪問修飾符,您需要在其成員的定義中包含其關鍵字,如下節所示。
私有成員
私有成員僅在其包含成員定義的類或物件內部可見。
以下是解釋私有成員的示例程式碼片段:
示例
class Outer { class Inner { private def f() { println("f") } class InnerMost { f() // OK } } (new Inner).f() // Error: f is not accessible }
在 Scala 中,訪問 (new Inner).f() 是非法的,因為 f 在 Inner 中宣告為私有,並且訪問不是來自 Inner 類內部。相反,Innermost 類中對 f 的第一次訪問是可以的,因為該訪問包含在 Inner 類的主體中。Java 將允許這兩種訪問,因為它允許外部類訪問其內部類的私有成員。
受保護成員
受保護成員只能從定義成員的類的子類訪問。
以下是解釋受保護成員的示例程式碼片段:
示例
package p { class Super { protected def f() { println("f") } } class Sub extends Super { f() } class Other { (new Super).f() // Error: f is not accessible } }
在 Sub 類中訪問 f 是可以的,因為 f 在“Super”類中宣告為受保護,並且“Sub”類是 Super 的子類。相反,不允許在“Other”類中訪問 f,因為“Other”類沒有從“Super”類繼承。在 Java 中,後者訪問仍然是被允許的,因為“Other”類與“Sub”類在同一個包中。
公共成員
與私有和受保護成員不同,不需要為公共成員指定 Public 關鍵字。公共成員沒有顯式修飾符。這些成員可以從任何地方訪問。
以下是解釋公共成員的示例程式碼片段:
示例
class Outer { class Inner { def f() { println("f") } class InnerMost { f() // OK } } (new Inner).f() // OK because now f() is public }
保護作用域
Scala 中的訪問修飾符可以使用限定符進行增強。形式為 private[X] 或 protected[X] 的修飾符表示訪問對 X 是私有的或受保護的,“直到”X,其中 X 指定某個封閉的包、類或單例物件。
考慮以下示例:
示例
package society { package professional { class Executive { private[professional] var workDetails = null private[society] var friends = null private[this] var secrets = null def help(another : Executive) { println(another.workDetails) println(another.secrets) //ERROR } } } }
注意 - 以上示例中的以下幾點:
變數 workDetails 可被專業包內的任何類訪問。
變數 friends 可被社會包內的任何類訪問。
變數 secrets 只能在例項方法 (this) 內的隱式物件上訪問。
Scala - 運算子
運算子是一個符號,它告訴編譯器執行特定的數學或邏輯操作。Scala 擁有豐富的內建運算子,並提供以下型別的運算子:
- 算術運算子
- 關係運算符
- 邏輯運算子
- 位運算子
- 賦值運算子
本章將逐一檢查算術、關係、邏輯、位、賦值和其他運算子。
算術運算子
Scala 語言支援以下算術運算子。例如,假設變數 A 為 10,變數 B 為 20,則:
運算子 | 描述 | 示例 |
---|---|---|
+ | 將兩個運算元相加 | A + B 將得到 30 |
- | 從第一個運算元中減去第二個運算元 | A - B 將得到 -10 |
* | 將兩個運算元相乘 | A * B 將得到 200 |
/ | 將分子除以分母 | B / A 將得到 2 |
% | 模運算子找到一個數除以另一個數後的餘數 | B % A 將得到 0 |
關係運算符
Scala 語言支援以下關係運算符。例如,假設變數 A 為 10,變數 B 為 20,則:
運算子 | 描述 | 示例 |
---|---|---|
== | 檢查兩個運算元的值是否相等,如果相等,則條件為真。 | (A == B) 為假。 |
!= | 檢查兩個運算元的值是否相等,如果不相等,則條件為真。 | (A != B) 為真。 |
> | 檢查左側運算元的值是否大於右側運算元的值,如果大於,則條件為真。 | (A > B) 為假。 |
< | 檢查左側運算元的值是否小於右側運算元的值,如果小於,則條件為真。 | (A < B) 為真。 |
>= | 檢查左側運算元的值是否大於或等於右側運算元的值,如果大於或等於,則條件為真。 | (A >= B) 為假。 |
<= | 檢查左側運算元的值是否小於或等於右側運算元的值,如果小於或等於,則條件為真。 | (A <= B) 為真。 |
邏輯運算子
Scala 語言支援以下邏輯運算子。例如,假設變數 A 為 1,變數 B 為 0,則:
運算子 | 描述 | 示例 |
---|---|---|
&& | 稱為邏輯與運算子。如果兩個運算元都不為零,則條件為真。 | (A && B) 為假。 |
|| | 稱為邏輯或運算子。如果兩個運算元中的任何一個都不為零,則條件為真。 | (A || B) 為真。 |
! | 稱為邏輯非運算子。用於反轉其運算元的邏輯狀態。如果條件為真,則邏輯非運算子將使其為假。 | !(A && B) 為真。 |
位運算子
位運算子作用於位並執行逐位運算。&, | 和 ^ 的真值表如下:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假設 A = 60;B = 13;現在以二進位制格式,它們將如下所示:
A = 0011 1100 B = 0000 1101 ----------------------- A&B = 0000 1100 A|B = 0011 1101 A^B = 0011 0001 ~A = 1100 0011
下表列出了 Scala 語言支援的位運算子。假設變數 A 為 60,變數 B 為 13,則:
運算子 | 描述 | 示例 |
---|---|---|
& | 二元 AND 運算子:如果該位同時存在於兩個運算元中,則將其複製到結果中。 | (A & B) 將得到 12,即 0000 1100 |
| | 二元 OR 運算子:如果該位存在於任一運算元中,則將其複製到結果中。 | (A | B) 將得到 61,即 0011 1101 |
^ | 二元 XOR 運算子:如果該位僅在一個運算元中設定,則將其複製到結果中。 | (A ^ B) 將得到 49,即 0011 0001 |
~ | 二元反碼運算子:它是單目運算子,作用是“翻轉”位。 | (~A) 將得到 -61,由於是帶符號二進位制數,因此其二進位制補碼形式為 1100 0011。 |
<< | 二元左移運算子:左運算元的值的位位置向左移動右運算元指定的位數。 | A << 2 將得到 240,即 1111 0000 |
>> | 二元右移運算子:左運算元的值的位位置向右移動右運算元指定的位數。 | A >> 2 將得到 15,即 1111 |
>>> | 右移零填充運算子:左運算元的值向右移動右運算元指定的位數,移出的位用零填充。 | A >>>2 將得到 15,即 0000 1111 |
賦值運算子
Scala 語言支援以下賦值運算子:
運算子 | 描述 | 示例 |
---|---|---|
= | 簡單賦值運算子:將右運算元的值賦給左運算元。 | C = A + B 將 A + B 的值賦給 C |
+= | 加法賦值運算子:將右運算元加到左運算元上,並將結果賦給左運算元。 | C += A 等價於 C = C + A |
-= | 減法賦值運算子:從左運算元中減去右運算元,並將結果賦給左運算元。 | C -= A 等價於 C = C - A |
*= | 乘法賦值運算子:將右運算元乘以左運算元,並將結果賦給左運算元。 | C *= A 等價於 C = C * A |
/= | 除法賦值運算子:將左運算元除以右運算元,並將結果賦給左運算元。 | C /= A 等價於 C = C / A |
%= | 取模賦值運算子:對兩個運算元取模,並將結果賦給左運算元。 | C %= A 等價於 C = C % A |
<<= | 左移賦值運算子 | C <<= 2 等價於 C = C << 2 |
>>= | 右移賦值運算子 | C >>= 2 等價於 C = C >> 2 |
&= | 按位 AND 賦值運算子 | C &= 2 等價於 C = C & 2 |
^= | 按位異或賦值運算子 | C ^= 2 等價於 C = C ^ 2 |
|= | 按位或賦值運算子 | C |= 2 等價於 C = C | 2 |
Scala 中的運算子優先順序
運算子優先順序決定了表示式中項的組合方式。這會影響表示式的計算方式。某些運算子比其他運算子具有更高的優先順序;例如,乘法運算子的優先順序高於加法運算子:
例如,x = 7 + 3 * 2;此處,x 被賦值為 13,而不是 20,因為運算子 * 的優先順序高於 +,所以它先與 3*2 相乘,然後加到 7 中。
請查看下錶。優先順序最高的運算子出現在表的上方,優先順序最低的運算子出現在表的下方。在一個表示式中,優先順序較高的運算子將首先計算。
類別 | 運算子 | 結合性 |
---|---|---|
字尾 | () [] | 從左到右 |
一元 | ! ~ | 從右到左 |
乘法 | * / % | 從左到右 |
加法 | + - | 從左到右 |
移位 | >> >>> << | 從左到右 |
關係 | > >= < <= | 從左到右 |
相等 | == != | 從左到右 |
按位 AND | & | 從左到右 |
按位 XOR | ^ | 從左到右 |
按位 OR | | | 從左到右 |
邏輯 AND | && | 從左到右 |
邏輯 OR | || | 從左到右 |
賦值 | = += -= *= /= %= >>= <<= &= ^= |= | 從右到左 |
逗號 | , | 從左到右 |
Scala - IF ELSE 語句
本章將引導您學習 Scala 程式設計中的條件構造語句。以下是大多數程式語言中常見的典型決策 IF...ELSE 結構的一般形式。
流程圖
以下是條件語句的流程圖。

if 語句
“if”語句由一個布林表示式和一個或多個語句組成。
語法
“if”語句的語法如下。
if(Boolean_expression) { // Statements will execute if the Boolean expression is true }
如果布林表示式計算結果為真,則將執行“if”表示式內的程式碼塊。如果不是,則將執行“if”表示式結束後的第一組程式碼(右大括號之後)。
嘗試以下示例程式以瞭解 Scala 程式語言中的條件表示式(if 表示式)。
示例
object Demo { def main(args: Array[String]) { var x = 10; if( x < 20 ){ println("This is if statement"); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
This is if statement
If-else 語句
“if”語句後面可以跟一個可選的else語句,當布林表示式為假時執行。
語法
if...else 的語法為:
if(Boolean_expression){ //Executes when the Boolean expression is true } else{ //Executes when the Boolean expression is false }
嘗試以下示例程式以瞭解 Scala 程式語言中的條件語句(if-else 語句)。
示例
object Demo { def main(args: Array[String]) { var x = 30; if( x < 20 ){ println("This is if statement"); } else { println("This is else statement"); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
This is else statement
If-else-if-else 語句
“if”語句後面可以跟一個可選的“else if...else”語句,這對於使用單個 if...else if 語句測試各種條件非常有用。
使用 if、else if、else 語句時,需要記住幾點。
一個“if”可以有零個或一個 else,並且它必須位於任何 else if 之後。
一個“if”可以有零個到多個 else if,並且它們必須位於 else 之前。
一旦 else if 成功,就不會測試其餘的 else if 或 else。
語法
以下是“if...else if...else”的語法:
if(Boolean_expression 1){ //Executes when the Boolean expression 1 is true } else if(Boolean_expression 2){ //Executes when the Boolean expression 2 is true } else if(Boolean_expression 3){ //Executes when the Boolean expression 3 is true } else { //Executes when the none of the above condition is true. }
嘗試以下示例程式以瞭解 Scala 程式語言中的條件語句(if-else-if-else 語句)。
示例
object Demo { def main(args: Array[String]) { var x = 30; if( x == 10 ){ println("Value of X is 10"); } else if( x == 20 ){ println("Value of X is 20"); } else if( x == 30 ){ println("Value of X is 30"); } else{ println("This is else statement"); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Value of X is 30
巢狀 if-else 語句
巢狀if-else語句始終是合法的,這意味著您可以在另一個if或else-if語句內使用一個if或else-if語句。
語法
巢狀 if-else 的語法如下:
if(Boolean_expression 1){ //Executes when the Boolean expression 1 is true if(Boolean_expression 2){ //Executes when the Boolean expression 2 is true } }
嘗試以下示例程式以瞭解 Scala 程式語言中的條件語句(巢狀 if 語句)。
示例
object Demo { def main(args: Array[String]) { var x = 30; var y = 10; if( x == 30 ){ if( y == 10 ){ println("X = 30 and Y = 10"); } } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
X = 30 and Y = 10
Scala - 迴圈語句
本章將引導您學習 Scala 程式語言中的迴圈控制結構。
可能存在需要多次執行程式碼塊的情況。通常,語句按順序執行:函式中的第一個語句首先執行,然後是第二個語句,依此類推。
程式語言提供各種控制結構,允許更復雜的執行路徑。
迴圈語句允許我們多次執行語句或語句組,以下是大多數程式語言中迴圈語句的一般形式:
流程圖

Scala 程式語言提供以下型別的迴圈來處理迴圈需求。單擊表中的以下連結以檢查其詳細資訊。
序號 | 迴圈型別和描述 |
---|---|
1 |
在給定條件為真的情況下重複語句或語句組。它在執行迴圈體之前測試條件。 |
2 |
類似於 while 語句,不同之處在於它在迴圈體末尾測試條件。 |
3 |
多次執行一系列語句,並縮寫管理迴圈變數的程式碼。 |
迴圈控制語句
迴圈控制語句會改變其正常執行順序。當執行離開作用域時,在該作用域中建立的所有自動物件都將被銷燬。因此,Scala 不像 Java 那樣支援break或continue語句,但從 Scala 2.8 版本開始,有一種方法可以中斷迴圈。單擊以下連結以檢查詳細資訊。
序號 | 控制語句和描述 |
---|---|
1 |
終止迴圈語句並將執行轉移到迴圈之後緊跟的語句。 |
無限迴圈
如果條件永不為假,則迴圈將變為無限迴圈。如果您使用的是 Scala,則while迴圈是實現無限迴圈的最佳方法。
以下程式實現了無限迴圈。
示例
object Demo { def main(args: Array[String]) { var a = 10; // An infinite loop. while( true ){ println( "Value of a: " + a ); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
如果您執行上述程式碼,它將進入無限迴圈,您可以按 Ctrl + C 鍵終止。
Value of a: 10 Value of a: 10 Value of a: 10 Value of a: 10 …………….
Scala - 函式
函式是一組執行任務的語句。您可以將程式碼分成單獨的函式。您如何將程式碼劃分為不同的函式取決於您自己,但從邏輯上講,劃分通常是讓每個函式執行特定任務。
Scala 既有函式也有方法,我們互換使用術語“方法”和“函式”,區別很小。Scala 方法是類的組成部分,它有名稱、簽名、可選的一些註釋和一些位元組碼,而 Scala 中的函式是一個完整的物件,可以賦值給變數。換句話說,定義為某個物件成員的函式稱為方法。
函式定義可以出現在原始檔的任何位置,Scala 允許巢狀函式定義,即其他函式定義內的函式定義。需要注意的最重要一點是 Scala 函式的名稱可以包含 +、++、~、&、-、--、\、/、: 等字元。
函式宣告
Scala 函式宣告具有以下形式:
def functionName ([list of parameters]) : [return type]
如果您不使用等號和方法體,則方法將隱式宣告為抽象。
函式定義
Scala 函式定義具有以下形式:
語法
def functionName ([list of parameters]) : [return type] = { function body return [expr] }
此處,返回型別可以是任何有效的 Scala 資料型別,引數列表將是用逗號分隔的變數列表,引數列表和返回型別都是可選的。與 Java 非常相似,如果函式返回值,則可以使用return語句以及表示式。以下是將新增兩個整數並返回其和的函式:
語法
object add { def addInt( a:Int, b:Int ) : Int = { var sum:Int = 0 sum = a + b return sum } }
不返回任何內容的函式可以返回一個Unit,它等價於 Java 中的void,表示函式不返回任何內容。在 Scala 中不返回任何內容的函式稱為過程。
語法
語法如下:
object Hello{ def printMe( ) : Unit = { println("Hello, Scala!") } }
呼叫函式
Scala 提供了許多呼叫方法的語法變體。以下是呼叫方法的標準方法:
functionName( list of parameters )
如果使用物件的例項呼叫函式,則可以使用類似於 Java 的點表示法,如下所示:
[instance.]functionName( list of parameters )
嘗試以下示例程式來定義和呼叫同一個函式。
示例
object Demo { def main(args: Array[String]) { println( "Returned Value : " + addInt(5,7) ); } def addInt( a:Int, b:Int ) : Int = { var sum:Int = 0 sum = a + b return sum } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Returned Value : 12
Scala 函式是 Scala 程式設計的核心,這就是 Scala 被認為是函數語言程式設計語言的原因。以下是 Scala 程式設計師應該理解的幾個與 Scala 函式相關的重要的概念。
Scala - 閉包
閉包是一個函式,其返回值取決於在此函式外部宣告的一個或多個變數的值。
以下程式碼片段使用了匿名函式。
val multiplier = (i:Int) => i * 10
這裡函式體中唯一使用的變數 i * 10 是 i,它被定義為函式的引數。嘗試以下程式碼:
val multiplier = (i:Int) => i * factor
multiplier 中有兩個自由變數:i 和 factor。其中之一,i,是函式的形式引數。因此,每次呼叫 multiplier 時,它都會繫結到一個新值。但是,factor 不是形式引數,那麼它是什麼呢?讓我們再新增一行程式碼。
var factor = 3 val multiplier = (i:Int) => i * factor
現在factor引用了函式外部但在封閉作用域中的變數。函式引用factor並在每次呼叫時讀取其當前值。如果一個函式沒有外部引用,那麼它就自身閉包。不需要外部上下文。
嘗試以下示例程式。
示例
object Demo { def main(args: Array[String]) { println( "multiplier(1) value = " + multiplier(1) ) println( "multiplier(2) value = " + multiplier(2) ) } var factor = 3 val multiplier = (i:Int) => i * factor }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
multiplier(1) value = 3 multiplier(2) value = 6
Scala - 字串
本章將帶您瞭解 Scala 字串。在 Scala 中,與 Java 一樣,字串是不可變物件,即無法修改的物件。另一方面,可以修改的物件(如陣列)稱為可變物件。字串是非常有用的物件,在本節的其餘部分中,我們將介紹java.lang.String類的重要方法。
建立字串
可以使用以下程式碼建立字串:
var greeting = "Hello world!"; or var greeting:String = "Hello world!";
每當編譯器在程式碼中遇到字串字面量時,它都會建立一個具有其值的 String 物件,在本例中為“Hello world!”。如上所示,也可以使用 String 關鍵字進行替代宣告。
嘗試以下示例程式。
示例
object Demo { val greeting: String = "Hello, world!" def main(args: Array[String]) { println( greeting ) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Hello, world!
如前所述,String 類是不可變的。建立 String 物件後便無法更改。如果需要對字元的字串進行大量修改,則可以使用 Scala 中提供的 StringBuilder 類!
字串長度
用於獲取有關物件資訊的方法稱為訪問器方法。可用於字串的一種訪問器方法是 length() 方法,它返回字串物件中包含的字元數。
使用以下程式碼段查詢字串的長度:
示例
object Demo { def main(args: Array[String]) { var palindrome = "Dot saw I was Tod"; var len = palindrome.length(); println( "String Length is : " + len ); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
String Length is : 17
連線字串
String 類包含一個用於連線兩個字串的方法:
string1.concat(string2);
這將返回一個新字串,該字串是 string1,其中 string2 新增到其末尾。您也可以將 concat() 方法與字串字面量一起使用,如下所示:
"My name is ".concat("Zara");
字串通常使用 + 運算子連線,如下所示:
"Hello," + " world" + "!"
結果為:
"Hello, world!"
以下程式碼行用於查詢字串長度。
示例
object Demo { def main(args: Array[String]) { var str1 = "Dot saw I was "; var str2 = "Tod"; println("Dot " + str1 + str2); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Dot Dot saw I was Tod
建立格式化字串
您可以使用 printf() 和 format() 方法以格式化的數字列印輸出。String 類有一個等效的類方法 format(),它返回 String 物件而不是 PrintStream 物件。
嘗試以下示例程式,它使用了 printf() 方法:
示例
object Demo { def main(args: Array[String]) { var floatVar = 12.456 var intVar = 2000 var stringVar = "Hello, Scala!" var fs = printf("The value of the float variable is " + "%f, while the value of the integer " + "variable is %d, and the string" + "is %s", floatVar, intVar, stringVar); println(fs) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
The value of the float variable is 12.456000, while the value of the integer variable is 2000, and the string is Hello, Scala!()
字串插值
字串插值是在 Scala 程式語言中建立字串的新方法。此功能支援 Scala-2.10 及更高版本。字串插值:在處理字串字面量時直接嵌入變數引用的機制。
字串插值中有三種類型的實現(插值器)。
‘s’ 字串插值器
字面量 ‘s’ 允許在處理字串時直接使用變數,當您在其前面加上 ‘s’ 時。任何在作用域內的字串變數都可以與字串一起使用。以下是 ‘s’ 字串插值器的不同用法。
以下示例程式碼片段演示了在 println 語句中將 ‘s’ 插值器用於將字串變數 ($name) 附加到普通字串 (Hello) 的實現。
val name = “James” println(s “Hello, $name”) //output: Hello, James
字串插值器還可以處理任意表達式。以下程式碼片段使用 ‘s’ 字串插值器處理帶有任意表達式 (${1 + 1}) 的字串 (1 + 1)。任何任意表達式都可以嵌入在 ‘${}’ 中。
println(s “1 + 1 = ${1 + 1}”) //output: 1 + 1 = 2
嘗試以下實現 ‘s’ 插值器的示例程式。
示例
object Demo { def main(args: Array[String]) { val name = "James" println(s"Hello, $name") println(s"1 + 1 = ${1 + 1}") } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Hello, James 1 + 1 = 2
‘f’ 插值器
字面量 ‘f’ 插值器允許建立格式化的字串,類似於 C 語言中的 printf。使用 ‘f’ 插值器時,所有變數引用後都應跟隨printf樣式的格式說明符,例如 %d、%i、%f 等。
讓我們以附加浮點值 (height = 1.9d) 和字串變數 (name = “James”) 與普通字串為例。以下是實現 ‘f’ 插值器的程式碼片段。這裡 $name%s 用於列印(字串變數)James,$height%2.2f 用於列印(浮點值)1.90。
val height = 1.9d val name = "James" println(f"$name%s is $height%2.2f meters tall") //James is 1.90 meters tall
它是型別安全的(即)變數引用和後面的格式說明符應該匹配,否則會顯示錯誤。‘f’ 插值器使用 Java 中提供的字串格式實用程式(格式說明符)。預設情況下,變數引用後沒有 % 字元。它將假定為 %s(字串)。
‘raw’ 插值器
‘raw’ 插值器類似於 ‘s’ 插值器,只是它不對字串內的字面量執行任何轉義。表中的以下程式碼片段將 ‘s’ 和 ‘raw’ 插值器的用法區分開來。在 ‘s’ 用法的輸出中,‘\n’ 的效果是換行,而在 ‘raw’ 用法的輸出中,‘\n’ 將不起作用。它將列印包含跳脫字元的完整字串。
‘s’ 插值器用法 | ‘raw’ 插值器用法 |
---|---|
程式: object Demo { def main(args: Array[String]) { println(s"Result = \n a \n b") } } |
程式: object Demo { def main(args: Array[String]) { println(raw"Result = \n a \n b") } } |
輸出: Result = a b |
輸出: Result = \n a \n b |
字串方法
以下是java.lang.String類定義的方法,可以直接在您的 Scala 程式中使用:
序號 | 帶描述的方法 |
---|---|
1 |
char charAt(int index) 返回指定索引處的字元。 |
2 |
int compareTo(Object o) 將此字串與另一個物件進行比較。 |
3 |
int compareTo(String anotherString) 按字典順序比較兩個字串。 |
4 |
int compareToIgnoreCase(String str) 按字典順序比較兩個字串,忽略大小寫差異。 |
5 |
String concat(String str) 將指定的字串連線到此字串的末尾。 |
6 |
boolean contentEquals(StringBuffer sb) 當且僅當此字串表示與指定的 StringBuffer 相同的字元序列時返回 true。 |
7 |
static String copyValueOf(char[] data) 返回表示陣列中指定字元序列的字串。 |
8 |
static String copyValueOf(char[] data, int offset, int count) 返回表示陣列中指定字元序列的字串。 |
9 |
boolean endsWith(String suffix) 測試此字串是否以指定的 suffix 結尾。 |
10 |
boolean equals(Object anObject) 將此字串與指定的物件進行比較。 |
11 |
boolean equalsIgnoreCase(String anotherString) 將此字串與另一個字串進行比較,忽略大小寫。 |
12 |
byte getBytes() 使用平臺的預設字元集將此字串編碼為位元組序列,並將結果儲存到新的位元組陣列中。 |
13 |
byte[] getBytes(String charsetName) 使用指定的字元集將此字串編碼為位元組序列,並將結果儲存到新的位元組陣列中。 |
14 |
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 將字元從此字串複製到目標字元陣列中。 |
15 |
int hashCode() 返回此字串的雜湊碼。 |
16 |
int indexOf(int ch) 返回此字串中指定字元第一次出現的索引。 |
17 |
int indexOf(int ch, int fromIndex) 返回此字串中指定字元第一次出現的索引,從指定的索引開始搜尋。 |
18 |
int indexOf(String str) 返回此字串中指定子字串第一次出現的索引。 |
19 |
int indexOf(String str, int fromIndex) 返回此字串中指定子字串第一次出現的索引,從指定的索引開始。 |
20 |
String intern() 返回字串物件的規範表示。 |
21 |
int lastIndexOf(int ch) 返回此字串中指定字元最後一次出現的索引。 |
22 |
int lastIndexOf(int ch, int fromIndex) 返回此字串中指定字元最後一次出現的索引,從指定的索引向後搜尋。 |
23 |
int lastIndexOf(String str) 返回此字串中指定子字串最右邊出現的索引。 |
24 |
int lastIndexOf(String str, int fromIndex) 返回此字串中指定子字串最後一次出現的索引,從指定的索引向後搜尋。 |
25 |
int length() 返回此字串的長度。 |
26 |
boolean matches(String regex) 判斷此字串是否與給定的正則表示式匹配。 |
27 |
boolean regionMatches(boolean ignoreCase, int toffset, String other, int offset, int len) 測試兩個字串區域是否相等。 |
28 |
boolean regionMatches(int toffset, String other, int offset, int len) 測試兩個字串區域是否相等。 |
29 |
String replace(char oldChar, char newChar) 返回一個新字串,該字串是透過將此字串中所有出現的 oldChar 替換為 newChar 得到的。 |
30 |
String replaceAll(String regex, String replacement) 將此字串中與給定正則表示式匹配的每個子字串替換為給定的替換字串。 |
31 |
String replaceFirst(String regex, String replacement) 將此字串中與給定正則表示式匹配的第一個子字串替換為給定的替換字串。 |
32 |
String[] split(String regex) 根據給定正則表示式的匹配拆分此字串。 |
33 |
String[] split(String regex, int limit) 根據給定正則表示式的匹配拆分此字串。 |
34 |
boolean startsWith(String prefix) 測試此字串是否以指定的 prefix 開頭。 |
35 |
boolean startsWith(String prefix, int toffset) 測試此字串是否以從指定索引開始的指定 prefix 開頭。 |
36 |
CharSequence subSequence(int beginIndex, int endIndex) 返回一個新的字元序列,它是此序列的子序列。 |
37 |
String substring(int beginIndex) 返回一個新的字串,它是此字串的子字串。 |
38 |
String substring(int beginIndex, int endIndex) 返回一個新的字串,它是此字串的子字串。 |
39 |
char[] toCharArray() 將此字串轉換為新的字元陣列。 |
40 |
String toLowerCase() 使用預設語言環境的規則將此字串中的所有字元轉換為小寫。 |
41 |
String toLowerCase(Locale locale) 使用給定語言環境的規則將此字串中的所有字元轉換為小寫。 |
42 |
String toString() 返回此物件(它本身就是一個字串!)。 |
43 |
String toUpperCase() 使用預設語言環境的規則將此字串中的所有字元轉換為大寫。 |
44 |
String toUpperCase(Locale locale) 使用給定語言環境的規則將此字串中的所有字元轉換為大寫。 |
45 |
String trim() 返回字串的副本,其中省略了開頭和結尾的空格。 |
46 |
static String valueOf(primitive data type x) 返回傳遞的資料型別引數的字串表示形式。 |
Scala - 陣列
Scala 提供了一種資料結構——陣列,它儲存固定大小的相同型別元素的順序集合。陣列用於儲存資料集合,但通常將陣列視為相同型別的變數集合更有用。
與其宣告單獨的變數,例如 number0、number1……number99,不如宣告一個數組變數,例如 numbers,並使用 numbers[0]、numbers[1]……numbers[99] 來表示各個單獨的變數。本教程介紹如何宣告陣列變數、建立陣列以及使用索引變數處理陣列。陣列第一個元素的索引是數字零,最後一個元素的索引是元素總數減一。
宣告陣列變數
要在程式中使用陣列,必須宣告一個變數來引用陣列,並且必須指定該變數可以引用的陣列型別。
以下是宣告陣列變數的語法:
語法
var z:Array[String] = new Array[String](3) or var z = new Array[String](3)
這裡,z 被宣告為一個字串陣列,最多可以容納三個元素。可以為各個元素賦值,或者訪問各個元素,可以使用以下命令:
命令
z(0) = "Zara"; z(1) = "Nuha"; z(4/2) = "Ayan"
這裡,最後一個例子表明,一般來說,索引可以是任何產生整數的表示式。還有一種定義陣列的方法:
var z = Array("Zara", "Nuha", "Ayan")
下圖表示一個數組 **myList**。這裡,**myList** 包含十個雙精度值,索引從 0 到 9。

處理陣列
處理陣列元素時,我們經常使用迴圈控制結構,因為陣列中的所有元素都是相同型別,並且陣列的大小是已知的。
下面是一個示例程式,演示如何建立、初始化和處理陣列:
示例
object Demo { def main(args: Array[String]) { var myList = Array(1.9, 2.9, 3.4, 3.5) // Print all the array elements for ( x <- myList ) { println( x ) } // Summing all elements var total = 0.0; for ( i <- 0 to (myList.length - 1)) { total += myList(i); } println("Total is " + total); // Finding the largest element var max = myList(0); for ( i <- 1 to (myList.length - 1) ) { if (myList(i) > max) max = myList(i); } println("Max is " + max); } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
1.9 2.9 3.4 3.5 Total is 11.7 Max is 3.5
Scala 不直接支援各種陣列操作,並提供各種方法來處理任何維度的陣列。如果要使用不同的方法,則需要匯入 **Array._** 包。
多維陣列
在許多情況下,您需要定義和使用多維陣列(即元素為陣列的陣列)。例如,矩陣和表格是可以實現為二維陣列的結構示例。
以下是定義二維陣列的示例:
var myMatrix = ofDim[Int](3,3)
這是一個數組,它有三個元素,每個元素都是一個包含三個整數元素的陣列。
嘗試以下示例程式來處理多維陣列:
示例
import Array._ object Demo { def main(args: Array[String]) { var myMatrix = ofDim[Int](3,3) // build a matrix for (i <- 0 to 2) { for ( j <- 0 to 2) { myMatrix(i)(j) = j; } } // Print two dimensional array for (i <- 0 to 2) { for ( j <- 0 to 2) { print(" " + myMatrix(i)(j)); } println(); } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
0 1 2 0 1 2 0 1 2
連線陣列
嘗試以下示例,它使用 concat() 方法連線兩個陣列。您可以將多個數組作為引數傳遞給 concat() 方法。
示例
import Array._ object Demo { def main(args: Array[String]) { var myList1 = Array(1.9, 2.9, 3.4, 3.5) var myList2 = Array(8.9, 7.9, 0.4, 1.5) var myList3 = concat( myList1, myList2) // Print all the array elements for ( x <- myList3 ) { println( x ) } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
1.9 2.9 3.4 3.5 8.9 7.9 0.4 1.5
使用範圍建立陣列
使用 range() 方法生成一個包含給定範圍內遞增整數序列的陣列。您可以使用最終引數作為步長來建立序列;如果您不使用最終引數,則步長將假定為 1。
讓我們以建立範圍 (10, 20, 2) 的陣列為例:這意味著建立一個元素在 10 和 20 之間且範圍差為 2 的陣列。陣列中的元素為 10、12、14、16 和 18。
另一個示例:range(10, 20)。這裡沒有給出範圍差,因此預設情況下它假設為 1。它建立一個數組,其中包含 10 和 20 之間的元素,範圍差為 1。陣列中的元素為 10、11、12、13……和 19。
以下示例程式演示如何建立具有範圍的陣列。
示例
import Array._ object Demo { def main(args: Array[String]) { var myList1 = range(10, 20, 2) var myList2 = range(10,20) // Print all the array elements for ( x <- myList1 ) { print( " " + x ) } println() for ( x <- myList2 ) { print( " " + x ) } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
10 12 14 16 18 10 11 12 13 14 15 16 17 18 19
Scala 陣列方法
以下是您可以玩轉陣列時可以使用的重要方法。如上所示,在使用任何提到的方法之前,您必須匯入 **Array._** 包。有關可用方法的完整列表,請檢視 Scala 的官方文件。
序號 | 帶描述的方法 |
---|---|
1 |
def apply(x: T, xs: T*): Array[T] 建立一個 T 物件陣列,其中 T 可以是 Unit、Double、Float、Long、Int、Char、Short、Byte、Boolean。 |
2 |
def concat[T](xss: Array[T]*): Array[T] 將所有陣列連線到單個數組中。 |
3 |
def copy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int): Unit 將一個數組複製到另一個數組。等效於 Java 的 System.arraycopy(src, srcPos, dest, destPos, length)。 |
4 |
def empty[T]: Array[T] 返回長度為 0 的陣列 |
5 |
def iterate[T](start: T, len: Int)(f: (T) => T): Array[T] 返回一個數組,其中包含將函式重複應用於起始值的計算結果。 |
6 |
def fill[T](n: Int)(elem: => T): Array[T] 返回一個數組,其中包含多次執行某個元素計算的結果。 |
7 |
def fill[T](n1: Int, n2: Int)(elem: => T): Array[Array[T]] 返回一個二維陣列,其中包含多次執行某個元素計算的結果。 |
8 |
def iterate[T](start: T, len: Int)(f: (T) => T): Array[T] 返回一個數組,其中包含將函式重複應用於起始值的計算結果。 |
9 |
def ofDim[T](n1: Int): Array[T] 建立具有給定維度的陣列。 |
10 |
def ofDim[T](n1: Int, n2: Int): Array[Array[T]] 建立一個二維陣列 |
11 |
def ofDim[T](n1: Int, n2: Int, n3: Int): Array[Array[Array[T]]] 建立一個三維陣列 |
12 |
def range(start: Int, end: Int, step: Int): Array[Int] 返回一個數組,其中包含某個整數區間內等間距的值。 |
13 |
def range(start: Int, end: Int): Array[Int] 返回一個數組,其中包含一個範圍內遞增整數的序列。 |
14 |
def tabulate[T](n: Int)(f: (Int) => T): Array[T] 返回一個數組,其中包含給定函式在一系列從 0 開始的整數值上的值。 |
15 |
def tabulate[T](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] 返回一個二維陣列,其中包含給定函式在一系列從 0 開始的整數值上的值。 |
Scala - 集合
Scala 擁有豐富的集合庫。集合是事物的容器。這些容器可以是有序的,線性集合的專案,例如列表、元組、選項、對映等。集合可以有任意數量的元素,也可以限制為零個或一個元素(例如,Option)。
集合可以是 **嚴格的** 或 **惰性的**。惰性集合的元素在訪問之前可能不會佔用記憶體,例如 **Ranges**。此外,集合可以是 **可變的**(引用的內容可以更改)或 **不可變的**(引用引用的內容永遠不會更改)。請注意,不可變集合可以包含可變專案。
對於某些問題,可變集合效果更好,而對於其他問題,不可變集合效果更好。如有疑問,最好從不可變集合開始,如果需要可變集合,則稍後更改。
本章闡述了最常用的集合型別和這些集合上最常用的操作。
序號 | 帶說明的集合 |
---|---|
1 |
Scala 的 List[T] 是型別為 T 的連結串列。 |
2 |
集合是相同型別成對不同元素的集合。 |
3 |
對映是鍵/值對的集合。可以根據其鍵檢索任何值。 |
4 |
與陣列或列表不同,元組可以儲存不同型別的物件。 |
5 |
Option[T] 為給定型別的一個或零個元素提供一個容器。 |
6 |
迭代器不是集合,而是一種一次一個地訪問集合元素的方式。 |
Scala - 特質
特徵封裝了方法和欄位定義,然後可以透過將它們混合到類中來重用。與類繼承不同,在類繼承中,每個類必須從一個超類繼承,類可以混合任意數量的特徵。
特徵用於透過指定支援方法的簽名來定義物件型別。Scala 還允許部分實現特徵,但特徵可能沒有建構函式引數。
特徵定義看起來就像類定義一樣,只是它使用了關鍵字 **trait**。以下是特徵的基本示例語法。
語法
trait Equal { def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) }
此特徵包含兩個方法 **isEqual** 和 **isNotEqual**。在這裡,我們沒有為 isEqual 提供任何實現,而另一個方法有其實現。擴充套件特徵的子類可以為未實現的方法提供實現。因此,特徵與我們在 Java 中使用的 **抽象類** 非常相似。
讓我們假設一個包含兩個方法 **isEqual()** 和 **isNotEqual()** 的特徵 **Equal** 的示例。特徵 **Equal** 包含一個已實現的方法 **isEqual()**,因此當用戶定義的類 **Point** 擴充套件特徵 **Equal** 時,應該提供 **Point** 類中 **isEqual()** 方法的實現。
這裡需要了解 Scala 的兩個重要方法,它們在以下示例中使用。
**obj.isInstanceOf[Point]** 用於檢查 obj 型別和 Point 型別是否相同。
**obj.asInstanceOf[Point]** 表示透過獲取物件 obj 型別並將其作為 Point 型別返回來進行精確轉換。
嘗試以下示例程式來實現特徵。
示例
trait Equal { def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) } class Point(xc: Int, yc: Int) extends Equal { var x: Int = xc var y: Int = yc def isEqual(obj: Any) = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == y } object Demo { def main(args: Array[String]) { val p1 = new Point(2, 3) val p2 = new Point(2, 4) val p3 = new Point(3, 3) println(p1.isNotEqual(p2)) println(p1.isNotEqual(p3)) println(p1.isNotEqual(2)) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
true false true
值類和通用特徵
值類是 Scala 中一種新的機制,用於避免分配執行時物件。它包含一個只有一個 **val** 引數的主建構函式。它只包含方法 (def),不允許使用 var、val、巢狀類、特徵或物件。值類不能被另一個類擴充套件。這可以透過使用 AnyVal 擴充套件您的值類來實現。在沒有執行時開銷的情況下實現自定義資料型別的型別安全。
讓我們以值類 Weight、Height、Email、Age 等為例。對於所有這些示例,不需要在應用程式中分配記憶體。
值類不允許擴充套件特徵。為了允許值類擴充套件特徵,引入了擴充套件 **Any** 的 **通用特徵**。
示例
trait Printable extends Any { def print(): Unit = println(this) } class Wrapper(val underlying: Int) extends AnyVal with Printable object Demo { def main(args: Array[String]) { val w = new Wrapper(3) w.print() // actually requires instantiating a Wrapper instance } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
它將為您提供包裝類的雜湊碼。
Wrapper@13
何時使用特徵?
沒有明確的規則,但這裡有一些需要考慮的指導原則:
如果行為不會被重用,則將其設為具體類。畢竟它不是可重用的行為。
如果它可能在多個不相關的類中重用,則將其設為特徵。只有特徵才能混合到類層次結構的不同部分。
如果要在 Java 程式碼中從中 **繼承**,請使用抽象類。
如果您計劃以編譯形式分發它,並且您期望外部組編寫從中繼承的類,您可能會傾向於使用抽象類。
如果效率非常重要,則傾向於使用類。
Scala - 模式匹配
模式匹配是 Scala 中繼函式值和閉包之後第二常用的特性。Scala 在處理訊息時為模式匹配提供了強大的支援。
模式匹配包括一系列備選方案,每個備選方案都以關鍵字 **case** 開頭。每個備選方案都包含一個 **模式** 和一個或多個 **表示式**,如果模式匹配,則將對這些表示式求值。箭頭符號 => 將模式與表示式分開。
嘗試以下示例程式,該程式演示如何與整數值匹配。
示例
object Demo { def main(args: Array[String]) { println(matchTest(3)) } def matchTest(x: Int): String = x match { case 1 => "one" case 2 => "two" case _ => "many" } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
many
包含 case 語句的程式碼塊定義了一個函式,該函式將整數對映到字串。match 關鍵字提供了一種方便的方法來將函式(如上面的模式匹配函式)應用於物件。
嘗試以下示例程式,該程式將值與不同型別的模式匹配。
示例
object Demo { def main(args: Array[String]) { println(matchTest("two")) println(matchTest("test")) println(matchTest(1)) } def matchTest(x: Any): Any = x match { case 1 => "one" case "two" => 2 case y: Int => "scala.Int" case _ => "many" } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
2 many one
使用案例類進行匹配
**案例類** 是在模式匹配中與 case 表示式一起使用的特殊類。從語法上講,這些是帶有特殊修飾符的標準類:**case**。
嘗試以下操作,這是一個使用案例類的簡單模式匹配示例。
示例
object Demo { def main(args: Array[String]) { val alice = new Person("Alice", 25) val bob = new Person("Bob", 32) val charlie = new Person("Charlie", 32) for (person <- List(alice, bob, charlie)) { person match { case Person("Alice", 25) => println("Hi Alice!") case Person("Bob", 32) => println("Hi Bob!") case Person(name, age) => println( "Age: " + age + " year, name: " + name + "?") } } } case class Person(name: String, age: Int) }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Hi Alice! Hi Bob! Age: 32 year, name: Charlie?
新增 case 關鍵字會導致編譯器自動新增許多有用的功能。該關鍵字建議與模式匹配中的 case 表示式相關聯。
首先,編譯器會自動將建構函式引數轉換為不可變欄位 (vals)。val 關鍵字是可選的。如果您想要可變欄位,請使用 var 關鍵字。因此,我們的建構函式引數列表現在更短了。
其次,編譯器會自動為類實現equals、hashCode和toString方法,這些方法使用指定為建構函式引數的欄位。因此,我們不再需要自己的toString()方法了。
最後,Person類的正文也為空,因為沒有我們需要定義的方法!
Scala - 正則表示式
本章解釋Scala如何透過scala.util.matching
包中提供的Regex類支援正則表示式。
嘗試以下示例程式,我們將嘗試從語句中找出單詞Scala。
示例
import scala.util.matching.Regex object Demo { def main(args: Array[String]) { val pattern = "Scala".r val str = "Scala is Scalable and cool" println(pattern findFirstIn str) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Some(Scala)
我們建立一個字串,並呼叫其上的r()方法。Scala隱式地將字串轉換為RichString並呼叫該方法以獲取Regex例項。要查詢正則表示式的第一個匹配項,只需呼叫findFirstIn()方法。如果我們想查詢匹配單詞的所有出現,而不是隻查詢第一個出現,我們可以使用findAllIn()方法,如果目標字串中有多個Scala單詞,這將返回所有匹配單詞的集合。
您可以使用mkString()方法連線結果列表,並且可以使用管道符號(|)搜尋Scala的小寫和大寫形式,並且可以使用Regex建構函式代替r()方法來建立模式。
嘗試以下示例程式。
示例
import scala.util.matching.Regex object Demo { def main(args: Array[String]) { val pattern = new Regex("(S|s)cala") val str = "Scala is scalable and cool" println((pattern findAllIn str).mkString(",")) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Scala,scala
如果要替換匹配的文字,我們可以使用replaceFirstIn()替換第一個匹配項,或者使用replaceAllIn()替換所有匹配項。
示例
object Demo { def main(args: Array[String]) { val pattern = "(S|s)cala".r val str = "Scala is scalable and cool" println(pattern replaceFirstIn(str, "Java")) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Java is scalable and cool
正則表示式的構成
Scala繼承了Java的正則表示式語法,而Java又繼承了Perl的大部分功能。這裡只是一些示例,應該足以作為複習——
下表列出了Java中可用的所有正則表示式元字元語法。
子表示式 | 匹配 |
---|---|
^ | 匹配行首。 |
$ | 匹配行尾。 |
. | 匹配除換行符以外的任何單個字元。使用m選項允許它匹配換行符。 |
[...] | 匹配括號中的任何單個字元。 |
[^...] | 匹配括號中不存在的任何單個字元 |
\\A | 整個字串的開頭 |
\\z | 整個字串的結尾 |
\\Z | 整個字串的結尾,除了允許的最終換行符。 |
re* | 匹配前面表示式的0個或多個出現。 |
re+ | 匹配前面表示式的1個或多個出現 |
re? | 匹配前面表示式的0個或1個出現。 |
re{ n} | 精確匹配前面表示式的n個出現。 |
re{ n,} | 匹配n個或更多前面表示式的出現。 |
re{ n, m} | 匹配前面表示式的至少n個和最多m個出現。 |
a|b | 匹配a或b。 |
(re) | 對正則表示式進行分組並記住匹配的文字。 |
(?: re) | 對正則表示式進行分組而不記住匹配的文字。 |
(?> re) | 匹配不回溯的獨立模式。 |
\\w | 匹配單詞字元。 |
\\W | 匹配非單詞字元。 |
\\s | 匹配空格。相當於[\t\n\r\f]。 |
\\S | 匹配非空格。 |
\\d | 匹配數字。相當於[0-9]。 |
\\D | 匹配非數字。 |
\\A | 匹配字串的開頭。 |
\\Z | 匹配字串的結尾。如果存在換行符,則匹配換行符之前的部分。 |
\\z | 匹配字串的結尾。 |
\\G | 匹配上次匹配結束的位置。 |
\\n | 反向引用到捕獲組編號“n” |
\\b | 在括號之外匹配單詞邊界。在括號內匹配退格符 (0x08)。 |
\\B | 匹配非單詞邊界。 |
\\n, \\t, etc. | 匹配換行符、回車符、製表符等。 |
\\Q | 轉義(引用)直到\\E的所有字元 |
\\E | 結束由\\Q開始的引用 |
正則表示式示例
示例 | 描述 |
---|---|
. | 匹配除換行符之外的任何字元 |
[Rr]uby | 匹配“Ruby”或“ruby” |
rub[ye] | 匹配“ruby”或“rube” |
[aeiou] | 匹配任何一個小寫母音 |
[0-9] | 匹配任何數字;與[0123456789]相同 |
[a-z] | 匹配任何小寫ASCII字母 |
[A-Z] | 匹配任何大寫ASCII字母 |
[a-zA-Z0-9] | 匹配以上任何一個 |
[^aeiou] | 匹配除小寫母音之外的任何字元 |
[^0-9] | 匹配除數字以外的任何字元 |
\\d | 匹配一個數字:[0-9] |
\\D | 匹配一個非數字:[ ^0-9] |
\\s | 匹配一個空格字元:[ \t\r\n\f] |
\\S | 匹配非空格:[ ^\t\r\n\f] |
\\w | 匹配單個單詞字元:[A-Za-z0-9_] |
\\W | 匹配一個非單詞字元:[ ^A-Za-z0-9_] |
ruby? | 匹配“rub”或“ruby”:y是可選的 |
ruby* | 匹配“rub”加上0個或多個y |
ruby+ | 匹配“rub”加上1個或多個y |
\\d{3} | 精確匹配3個數字 |
\\d{3,} | 匹配3個或更多數字 |
\\d{3,5} | 匹配3、4或5個數字 |
\\D\\d+ | 無分組:+ 重複 \\d |
(\\D\\d)+/ | 分組:+ 重複 \\D\d 對 |
([Rr]uby(, )?)+ | 匹配“Ruby”、“Ruby, ruby, ruby”等。 |
注意——上述字串中每個反斜槓都出現了兩次。這是因為在Java和Scala中,單個反斜槓是字串文字中的跳脫字元,而不是出現在字串中的常規字元。因此,您需要編寫“\\”而不是“\”才能在字串中獲得單個反斜槓。
嘗試以下示例程式。
示例
import scala.util.matching.Regex object Demo { def main(args: Array[String]) { val pattern = new Regex("abl[ae]\\d+") val str = "ablaw is able1 and cool" println((pattern findAllIn str).mkString(",")) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
able1
Scala - 異常處理
Scala的異常與Java等許多其他語言中的異常一樣。方法不會以通常的方式返回值,而是透過丟擲異常來終止。但是,Scala實際上沒有檢查異常。
當您要處理異常時,您可以像在Java中一樣使用try{...}catch{...}塊,只是catch塊使用匹配來識別和處理異常。
丟擲異常
丟擲異常看起來與Java中一樣。您建立一個異常物件,然後使用throw關鍵字丟擲它,如下所示。
throw new IllegalArgumentException
捕獲異常
Scala允許您在一個塊中try/catch任何異常,然後使用case塊對其進行模式匹配。嘗試以下示例程式來處理異常。
示例
import java.io.FileReader import java.io.FileNotFoundException import java.io.IOException object Demo { def main(args: Array[String]) { try { val f = new FileReader("input.txt") } catch { case ex: FileNotFoundException =>{ println("Missing file exception") } case ex: IOException => { println("IO Exception") } } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Missing file exception
此try-catch表示式的行為與其他具有異常的語言相同。執行主體,如果它丟擲異常,則依次嘗試每個catch子句。
finally子句
如果要強制執行某些程式碼,無論表示式如何終止,都可以使用finally子句包裝表示式。嘗試以下程式。
示例
import java.io.FileReader import java.io.FileNotFoundException import java.io.IOException object Demo { def main(args: Array[String]) { try { val f = new FileReader("input.txt") } catch { case ex: FileNotFoundException => { println("Missing file exception") } case ex: IOException => { println("IO Exception") } } finally { println("Exiting finally...") } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Missing file exception Exiting finally...
Scala - 提取器
Scala中的提取器是一個物件,它具有名為unapply的方法作為其成員之一。unapply方法的目的是匹配一個值並將其分解。通常,提取器物件還定義了一個用於構建值的雙重方法apply,但這並非必需。
示例
讓我們以一個同時定義apply和unapply方法的物件為例。apply方法的含義與往常一樣:它將Test轉換為可以像方法一樣應用於括號中引數的物件。因此,您可以編寫Test("Zara", "gmail.com")來構造字串"Zara@gmail.com"。
unapply方法將Test類轉換為提取器,它反轉了apply的構造過程。apply接收兩個字串並從中形成電子郵件地址字串,而unapply接收一個電子郵件地址並返回可能兩個字串:地址的使用者和域。
unapply還必須處理給定字串不是電子郵件地址的情況。這就是為什麼unapply返回字串對的Option型別。其結果是,如果字串str是具有給定使用者和域部分的電子郵件地址,則為Some(user, domain),如果str不是電子郵件地址,則為None。以下是一些示例。
語法
unapply("Zara@gmail.com") equals Some("Zara", "gmail.com") unapply("Zara Ali") equals None
以下示例程式顯示了電子郵件地址的提取器物件。
示例
object Demo { def main(args: Array[String]) { println ("Apply method : " + apply("Zara", "gmail.com")); println ("Unapply method : " + unapply("Zara@gmail.com")); println ("Unapply method : " + unapply("Zara Ali")); } // The injection method (optional) def apply(user: String, domain: String) = { user +"@"+ domain } // The extraction method (mandatory) def unapply(str: String): Option[(String, String)] = { val parts = str split "@" if (parts.length == 2){ Some(parts(0), parts(1)) } else { None } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Apply method : Zara@gmail.com Unapply method : Some((Zara,gmail.com)) Unapply method : None
使用提取器的模式匹配
當類例項後跟帶有零個或多個引數的列表的括號時,編譯器會在該例項上呼叫apply方法。我們可以在物件和類中定義apply。
如上所述,unapply方法的目的是提取我們正在尋找的特定值。它執行與apply相反的操作。當使用match語句比較提取器物件時,將自動執行unapply方法。
嘗試以下示例程式。
示例
object Demo { def main(args: Array[String]) { val x = Demo(5) println(x) x match { case Demo(num) => println(x+" is bigger two times than "+num) //unapply is invoked case _ => println("i cannot calculate") } } def apply(x: Int) = x*2 def unapply(z: Int): Option[Int] = if (z%2==0) Some(z/2) else None }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
10 10 is bigger two times than 5
Scala - 檔案 I/O
Scala可以自由使用任何Java物件,而java.io.File是可以用於Scala程式設計中讀取和寫入檔案的物件之一。
以下是一個寫入檔案的示例程式。
示例
import java.io._ object Demo { def main(args: Array[String]) { val writer = new PrintWriter(new File("test.txt" )) writer.write("Hello Scala") writer.close() } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
它將在程式所在位置的當前目錄中建立一個名為Demo.txt的檔案。以下是該檔案的內容。
輸出
Hello Scala
從命令列讀取一行
有時您需要從螢幕讀取使用者輸入,然後繼續進行一些進一步的處理。以下示例程式向您展示如何從命令列讀取輸入。
示例
object Demo { def main(args: Array[String]) { print("Please enter your input : " ) val line = Console.readLine println("Thanks, you just typed: " + line) } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Please enter your input : Scala is great Thanks, you just typed: Scala is great
讀取檔案內容
從檔案讀取非常簡單。您可以使用Scala的Source類及其伴生物件來讀取檔案。以下是向您展示如何從我們之前建立的"Demo.txt"檔案讀取的示例。
示例
import scala.io.Source object Demo { def main(args: Array[String]) { println("Following is the content read:" ) Source.fromFile("Demo.txt" ).foreach { print } } }
將上述程式儲存在Demo.scala中。以下命令用於編譯和執行此程式。
命令
\>scalac Demo.scala \>scala Demo
輸出
Following is the content read: Hello Scala