Scala 導覽

基礎

語言

在此頁面中,我們將介紹 Scala 的基礎知識。

在瀏覽器中嘗試 Scala

您可以在瀏覽器中使用 Scastie 執行 Scala。這是一種輕鬆、無需設定的方式,可以用來試驗 Scala 程式碼片段

  1. 前往 Scastie
  2. 在左側窗格中貼上 println("Hello, world!")
  3. 按一下執行。輸出會顯示在右側窗格中。

Scastie 已整合此文件中的部分程式碼範例;如果您在以下程式碼範例中看到執行按鈕,請按一下它以直接試驗程式碼。

表達式

表達式是可計算的陳述式

1 + 1

您可以使用 println 輸出表達式的結果

println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!

您可以使用 val 關鍵字命名表達式的結果

val x = 1 + 1
println(x) // 2

命名的結果,例如此處的 x,稱為值。參照值不會重新計算它。

值無法重新指派

x = 3 // This does not compile.

值的類型可以省略並 推斷,或可以明確陳述

val x: Int = 1 + 1

請注意類型宣告 Int 如何出現在識別碼 x 之後。您還需要一個 :

變數

變數類似於值,但您可以重新指派它們。您可以使用 var 關鍵字定義變數。

var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9

與值一樣,變數的類型可以省略並 推斷,或可以明確陳述

var x: Int = 1 + 1

區塊

你可以透過使用 {} 將表達式包圍起來,來組合表達式。我們稱之為區塊。

區塊中最後一個表達式的結果,也是整個區塊的結果

println({
  val x = 1 + 1
  x + 1
}) // 3

函式

函式是有參數且會接收引數的表達式。

你可以定義一個匿名函式(即沒有名稱的函式),傳回給定的整數加一

(x: Int) => x + 1

=> 的左側是參數清單。右側是包含參數的表達式。

你也可以命名函式

val addOne = (x: Int) => x + 1
println(addOne(1)) // 2

一個函式可以有多個參數

val add = (x: Int, y: Int) => x + y
println(add(1, 2)) // 3

或者它可以完全沒有參數

val getTheAnswer = () => 42
println(getTheAnswer()) // 42

方法

方法看起來和行為都很類似於函式,但它們之間有一些關鍵的差異。

方法使用 def 關鍵字定義。 def 後面接著一個名稱、參數清單、回傳型別和主體

def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3

請注意回傳型別 Int 是在參數清單和 : 之後宣告的。

一個方法可以接受多個參數清單

def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9

或者完全沒有參數清單

def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")

還有一些其他差異,但就目前而言,你可以將方法視為類似於函式的東西。

方法也可以有多行表達式

def getSquareString(input: Double): String = {
  val square = input * input
  square.toString
}
println(getSquareString(2.5)) // 6.25
def getSquareString(input: Double): String =
  val square = input * input
  square.toString

println(getSquareString(2.5)) // 6.25

主體中的最後一個表達式是方法的回傳值。(Scala 確實有一個 return 關鍵字,但它很少使用。)

類別

你可以使用 class 關鍵字,接著是其名稱和建構函式參數來定義類別

class Greeter(prefix: String, suffix: String) {
  def greet(name: String): Unit =
    println(prefix + name + suffix)
}
class Greeter(prefix: String, suffix: String):
  def greet(name: String): Unit =
    println(prefix + name + suffix)

方法 greet 的回傳型別是 Unit,表示沒有有意義的東西可以回傳。它與 Java 和 C 中的 void 類似。(不同之處在於,由於每個 Scala 表達式都必須具有一些值,因此實際上有一個 Unit 型別的單例值,寫成 ()。它不攜帶任何資訊。)

在 Scala 2 中,你可以使用 new 關鍵字建立類別的實例。然而,在 Scala 3 中,由於 通用套用方法,不需要 new 關鍵字

val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
val greeter = Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!

我們將在 稍後深入探討類別。

案例類別

Scala 有一種特殊的類別類型稱為「案例」類別。預設情況下,案例類別的實例是不可變的,並且它們是透過值來比較(與類別不同,類別的實例是透過參考來比較)。這使得它們對於 模式比對 更有用。

你可以使用 case class 關鍵字來定義案例類別

case class Point(x: Int, y: Int)

你可以建立案例類別,而不需要 new 關鍵字

val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)

案例類別的實例是透過值來比較,而不是透過參考

if (point == anotherPoint) {
  println(s"$point and $anotherPoint are the same.")
} else {
  println(s"$point and $anotherPoint are different.")
} // Point(1,2) and Point(1,2) are the same.

if (point == yetAnotherPoint) {
  println(s"$point and $yetAnotherPoint are the same.")
} else {
  println(s"$point and $yetAnotherPoint are different.")
} // Point(1,2) and Point(2,2) are different.
if point == anotherPoint then
  println(s"$point and $anotherPoint are the same.")
else
  println(s"$point and $anotherPoint are different.")
// ==> Point(1,2) and Point(1,2) are the same.

if point == yetAnotherPoint then
  println(s"$point and $yetAnotherPoint are the same.")
else
  println(s"$point and $yetAnotherPoint are different.")
// ==> Point(1,2) and Point(2,2) are different.

還有更多關於案例類別的內容我們想要介紹,我們相信你會愛上它們!我們會在 稍後深入探討。

物件

物件是它們自己定義的單一實例。你可以將它們視為它們自己類別的單例。

你可以使用 object 關鍵字來定義物件

object IdFactory {
  private var counter = 0
  def create(): Int = {
    counter += 1
    counter
  }
}
object IdFactory:
  private var counter = 0
  def create(): Int =
    counter += 1
    counter

你可以透過參照物件的名稱來存取物件

val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2

我們會在 稍後深入探討物件。

特質

特質是包含特定欄位和方法的抽象資料類型。在 Scala 繼承中,一個類別只能延伸一個其他類別,但它可以延伸多個特質。

你可以使用 trait 關鍵字來定義特質

trait Greeter {
  def greet(name: String): Unit
}
trait Greeter:
  def greet(name: String): Unit

特質也可以有預設實作

trait Greeter {
  def greet(name: String): Unit =
    println("Hello, " + name + "!")
}
trait Greeter:
  def greet(name: String): Unit =
    println("Hello, " + name + "!")

你可以使用 extends 關鍵字來延伸特質,並使用 override 關鍵字來覆寫實作

class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
  override def greet(name: String): Unit = {
    println(prefix + name + postfix)
  }
}

val greeter = new DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!

val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?
class DefaultGreeter extends Greeter

class CustomizableGreeter(prefix: String, postfix: String) extends Greeter:
  override def greet(name: String): Unit =
    println(prefix + name + postfix)

val greeter = DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!

val customGreeter = CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?

這裡,DefaultGreeter 僅延伸一個單一特質,但它可以延伸多個特質。

我們會在 稍後深入探討特質。

程式進入點

main 方法是 Scala 程式的進入點。Java 虛擬機器需要一個名為 main 的 main 方法,它接受一個引數:字串陣列。

在 Scala 2 中,你必須手動定義一個 main 方法。使用物件,你可以如下定義 main 方法

object Main {
  def main(args: Array[String]): Unit =
    println("Hello, Scala developer!")
}

在 Scala 3 中,使用 @main 註解,可以如下從一個方法自動產生一個 main 方法

@main def hello() = println("Hello, Scala developer!")

更多資源

此頁面的貢獻者