自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Scala語言入門:初學(xué)者的基礎(chǔ)語法指南

開發(fā)
本文將帶領(lǐng)大家逐步了解Scala的基礎(chǔ)知識(shí),無論你是編程新手還是想要擴(kuò)展技能集的專業(yè)開發(fā)者,都可以在這篇文章中找到有用的信息。

在計(jì)算機(jī)編程的世界里,Scala是一個(gè)不可或缺的語言。

作為一種在Java虛擬機(jī)(JVM)上運(yùn)行的靜態(tài)類型編程語言,Scala結(jié)合了面向?qū)ο蠛秃瘮?shù)式編程的特性,使它既有強(qiáng)大的表達(dá)力又具備優(yōu)秀的型態(tài)控制。

對(duì)于初學(xué)者來說,理解Scala的基本語法是掌握這門語言的關(guān)鍵步驟。本文將帶領(lǐng)大家逐步了解Scala的基礎(chǔ)知識(shí),無論你是編程新手還是想要擴(kuò)展技能集的專業(yè)開發(fā)者,都可以在這篇文章中找到有用的信息。

先分享Scala的官方網(wǎng)站:https://docs.scala-lang.org/。

大部分的學(xué)習(xí)資料都可以在這找到,語言支持切換中文,非常友好。

另外我們可以使用Scastie網(wǎng)站,在瀏覽器上直接運(yùn)行Scala代碼進(jìn)行調(diào)試:https://scastie.scala-lang.org/。

Scala & Java

Scala語言和Java語言有許多相似之處,但也有一些明顯的區(qū)別。

Scala語言來源于Java,它以Java虛擬機(jī)(JVM)為運(yùn)行環(huán)境,Scala源碼 (.scala)會(huì)編譯成.class文件。這意味著Scala程序可以與Java程序互操作,并且可以利用JVM的優(yōu)化和性能。

在語法上,Scala和Java有一些區(qū)別。

例如,在Scala中,一切皆為對(duì)象,而在Java中,基本類型、null、靜態(tài)方法等不是對(duì)象。在Scala中,成員變量/屬性必須顯示初始化,而在Java中可以不初始化。此外,在Scala中,異常處理采用Try-catch {case-case}-finally的方式,而在Java中采用Try-catch-catch-finally的方式。

Scala還有一些特有的概念,例如:惰性函數(shù)、伴生對(duì)象、特質(zhì)、偏函數(shù)等。這些概念都為Scala語言提供了更多的靈活性和表達(dá)能力。使得Scala語言非常適合用來開發(fā)大數(shù)據(jù)處理框架。此外,Scala語言的語法糖也非常甜,可以用更少的代碼量來實(shí)現(xiàn)相同的功能。

Scala安裝

先從安裝Scala說起,Scala的安裝也很簡單。

(1) 首先Idea安裝 Scala插件。

(2) 項(xiàng)目結(jié)構(gòu)里點(diǎn)擊全局庫,添加 Scala SDK進(jìn)行下載。

(3) 右鍵點(diǎn)擊添加到你要使用Scala的項(xiàng)目的項(xiàng)目庫,項(xiàng)目的庫里就會(huì)多出Scala的SDK。

到這就結(jié)束了,然后我們就可以在項(xiàng)目里使用Scala了。

新建一個(gè)Scala項(xiàng)目,運(yùn)行Hello Wrold試一下。

數(shù)據(jù)類型

Scala中的數(shù)據(jù)類型可以分為兩大類:值類型(AnyVal)和引用類型(AnyRef)。這兩種類型都是 Any 類型的子類。

值類型包括9種基本數(shù)據(jù)類型,分別是 Byte、Short、Int、Long、Float、Double、Char、Boolean 和 Unit。其中,前8種類型與Java中的基本數(shù)據(jù)類型相對(duì)應(yīng),而 Unit 類型表示無值,類似于Java中的 void。

引用類型包括所有非值類型的數(shù)據(jù)類型,例如字符串、數(shù)組、列表等。它們都是 AnyRef 類型的子類。

在Scala的數(shù)據(jù)類型層級(jí)結(jié)構(gòu)的底部,還有兩個(gè)特殊的數(shù)據(jù)類型:Nothing 和 Null。其中, Nothing 類型是所有類型的子類型,它沒有任何實(shí)例。而 Null 類型是所有引用類型的子類型,它只有一個(gè)實(shí)例:null。

語法

主方法是一個(gè)程序的入口點(diǎn)。JVM要求一個(gè)名為main的主方法,接受一個(gè)字符串?dāng)?shù)組的參數(shù)。你可以如下所示來定義一個(gè)主方法。

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

在Scala 2中,也可以通過創(chuàng)建一個(gè)擴(kuò)展App類的對(duì)象來定義主程序。例如:

object Main extends App {
  println("Hello, Scala developer!")
}

需要注意的是,這種方法在Scala 3中不再推薦使用。它們被新的@main方法取代了,這是在Scala 3中生成可以從命令行調(diào)用的程序的推薦方法。App目前仍以有限的形式存在,但它不支持命令行參數(shù),將來會(huì)被棄用。

1.val & var

在 Scala 中,val 和 var 都可以用來定義變量,但它們之間有一些重要的區(qū)別。

val 用于定義不可變變量,也就是說,一旦定義了一個(gè) val 變量,它的值就不能再被改變。例如:

val x = 1
// x = 2 // 這會(huì)報(bào)錯(cuò),因?yàn)椴荒芙o val 變量重新賦值

而 var 用于定義可變變量,它的值可以在定義后被改變。例如:

var y = 1
y = 2 // 這是合法的,因?yàn)?y 是一個(gè) var 變量

val和var的類型可以被推斷,或者你也可以顯式地聲明類型,例如:

val x: Int = 1 + 1
var x: Int = 1 + 1

在實(shí)際編程中,我們應(yīng)該盡量使用 val 來定義不可變變量,這樣可以提高代碼的可讀性和可維護(hù)性。只有在確實(shí)需要改變變量值的情況下,才應(yīng)該使用 var 來定義可變變量。

2.泛型

在Scala 中,使用方括號(hào) [] 來定義泛型類型。而在Java中是使用<>。

例如,下面這段代碼:

object Main extends App {
  trait Animal {
    def speak: String
  }

  class Dog extends Animal {
    def speak = "Woof!"
  }

  class Cat extends Animal {
    def speak = "Meow!"
  }

  class Parrot extends Animal {
    def speak = "Squawk!"
  }

  class AnimalShelter[A <: Animal] {
    private var animals: List[A] = Nil

    def addAnimal(animal: A): Unit = {
      animals = animal :: animals
    }

    def getAnimal: A = {
      val animal = animals.head
      animals = animals.tail
      animal
    }
  }

  val dogShelter = new AnimalShelter[Dog]
  dogShelter.addAnimal(new Dog)
  val dog: Dog = dogShelter.getAnimal
  println(dog.speak)

  val catShelter = new AnimalShelter[Cat]
  catShelter.addAnimal(new Cat)
  val cat: Cat = catShelter.getAnimal
  println(cat.speak)

  val parrotShelter = new AnimalShelter[Parrot]
  parrotShelter.addAnimal(new Parrot)
  val parrot: Parrot = parrotShelter.getAnimal
  println(parrot.speak)
}

輸出:
Woof!
Meow!
Squawk!

這個(gè)示例中,我們定義了一個(gè) Animal 特質(zhì)和三個(gè)實(shí)現(xiàn)了該特質(zhì)的類:Dog,Cat 和 Parrot。然后我們定義了一個(gè) AnimalShelter 類,它使用了泛型類型參數(shù) A,并且限制了 A 必須是 Animal 的子類型。這樣我們就可以創(chuàng)建不同類型的動(dòng)物收容所,比如 dogShelter,catShelter 和 parrotShelter,并且在添加和獲取動(dòng)物時(shí)保證類型安全。

3.包導(dǎo)入

import 語句用于導(dǎo)入其他包中的成員(類,特質(zhì),函數(shù)等)。使用相同包的成員不需要 import 語句。導(dǎo)入語句可以有選擇性:

import users._  // 導(dǎo)入包 users 中的所有成員
import users.User  // 導(dǎo)入類 User
import users.{User, UserPreferences}  // 僅導(dǎo)入選擇的成員
import users.{UserPreferences => UPrefs}  // 導(dǎo)入類并且設(shè)置別名

Scala 不同于 Java 的一點(diǎn)是 Scala 可以在任何地方使用導(dǎo)入:

def sqrtplus1(x: Int) = {
  import scala.math.sqrt
  sqrt(x) + 1.0
}

如果存在命名沖突并且你需要從項(xiàng)目的根目錄導(dǎo)入,請(qǐng)?jiān)诎Q前加上 root:

package accounts

import _root_.users._

注意:包 scala 和 java.lang 以及 object Predef 是默認(rèn)導(dǎo)入的。

4.包對(duì)象

在 Scala 中,包對(duì)象(Package Object)是一種特殊的對(duì)象,它與包同名,并且可以在包中定義一些公共的成員和方法,供包中的其他類和對(duì)象直接使用。包對(duì)象可以解決在包級(jí)別共享常量、類型別名、隱式轉(zhuǎn)換等問題。

在 Scala 中,可以使用 package 關(guān)鍵字定義一個(gè)包對(duì)象。包對(duì)象的文件名必須為 package.scala,并與包名一致。

下面是關(guān)于包對(duì)象的解釋和示例代碼:

// File: com/example/myapp/package.scala

package com.example

package object myapp {
  val appName: String = "MyApp"

  def printAppName(): Unit = {
    println(appName)
  }
}

在上述示例中,定義了一個(gè)包對(duì)象 myapp,位于包 com.example 下。在包對(duì)象中,我們定義了一個(gè)名為 appName 的常量和一個(gè)名為 printAppName 的方法。

這樣,我們就可以在包中的其他類和對(duì)象中直接使用 appName 和 printAppName,而無需導(dǎo)入或限定符。

下面是一個(gè)使用包對(duì)象的示例代碼:

package com.example.myapp

object Main {
  def main(args: Array[String]): Unit = {
    println(myapp.appName)  // 直接訪問包對(duì)象中的常量
    myapp.printAppName()    // 直接調(diào)用包對(duì)象中的方法
  }
}

在上述示例中,我們?cè)?nbsp;Main 對(duì)象中直接訪問了包對(duì)象 myapp 中的常量 appName 和方法 printAppName。由于包對(duì)象與包同名且位于同一包中,因此可以直接使用它們。

5.特質(zhì)

在Scala中,類是單繼承的,但是特質(zhì)(trait)可以多繼承。

這意味著,一個(gè)類只能繼承一個(gè)父類,但可以繼承多個(gè)特質(zhì)。這樣,從結(jié)果上看,就實(shí)現(xiàn)了多重繼承。

下面是一個(gè)例子:

trait A {
  def printA() = println("A")
}

trait B {
  def printB() = println("B")
}

class C extends A with B

object Main extends App {
  val c = new C
  c.printA()
  c.printB()
}

輸出:
A
B

例子中,定義了兩個(gè)特質(zhì) A 和 B,它們分別有一個(gè)方法 printA 和 printB。然后我們定義了一個(gè)類 C,它繼承了特質(zhì) A 和 B。這樣,類 C 就可以使用特質(zhì) A 和 B 中定義的方法了。

特質(zhì)也可以有默認(rèn)的實(shí)現(xiàn):

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

你可以使用extends關(guān)鍵字來繼承特質(zhì),使用override關(guān)鍵字來覆蓋默認(rèn)的實(shí)現(xiàn)。

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?

凡是需要特質(zhì)的地方,都可以由該特質(zhì)的子類型來替換。

import scala.collection.mutable.ArrayBuffer

trait Pet {
  val name: String
}

class Cat(val name: String) extends Pet
class Dog(val name: String) extends Pet

val dog = new Dog("Harry")
val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name))  // Prints Harry Sally

在這里 trait Pet 有一個(gè)抽象字段 name ,name 由Cat和Dog的構(gòu)造函數(shù)中實(shí)現(xiàn)。最后一行,我們能調(diào)用pet.name的前提是它必須在特質(zhì)Pet的子類型中得到了實(shí)現(xiàn)。

6.運(yùn)算符

在 Scala 中,運(yùn)算符是用于執(zhí)行特定操作的符號(hào)或標(biāo)記。Scala 具有豐富的運(yùn)算符,并且允許用戶自定義運(yùn)算符,以及在自定義類中使用運(yùn)算符。下面是關(guān)于定義和使用運(yùn)算符的解釋和示例代碼:

在 Scala 中,可以使用 def 關(guān)鍵字定義自定義運(yùn)算符。自定義運(yùn)算符可以是任何由字母、數(shù)字或下劃線組成的標(biāo)識(shí)符,以及一些特殊字符,例如 +、-、* 等。要定義一個(gè)運(yùn)算符,可以在方法名前面加上一個(gè)操作符,然后在方法體中實(shí)現(xiàn)相應(yīng)的邏輯。

下面是一個(gè)示例代碼:

class Vector2D(val x: Double, val y: Double) {
  def +(other: Vector2D): Vector2D = {
    new Vector2D(x + other.x, y + other.y)
  }
}

val v1 = new Vector2D(1.0, 2.0)
val v2 = new Vector2D(3.0, 4.0)
val sum = v1 + v2

println(sum.x) // 輸出:4.0
println(sum.y) // 輸出:6.0

在上述示例中,定義了一個(gè) Vector2D 類,表示二維向量。我們通過 val 關(guān)鍵字定義了 x 和 y 作為向量的坐標(biāo)。

然后,我們定義了一個(gè)自定義運(yùn)算符 +,它接受另一個(gè) Vector2D 對(duì)象作為參數(shù),并返回一個(gè)新的 Vector2D 對(duì)象。在方法體內(nèi),我們實(shí)現(xiàn)了向量的加法操作。

在主程序中,我們創(chuàng)建了兩個(gè) Vector2D 對(duì)象 v1 和 v2。然后,我們使用自定義的運(yùn)算符 + 來執(zhí)行向量的加法,并將結(jié)果賦值給 sum。

最后,我們打印出 sum 的 x 和 y 坐標(biāo),驗(yàn)證加法操作的結(jié)果。

我們可以像使用內(nèi)置運(yùn)算符一樣使用自定義運(yùn)算符。它們可以用于相應(yīng)類型的實(shí)例上,并按照定義的邏輯執(zhí)行操作。

下面是一個(gè)示例代碼:

val num1 = 10
val num2 = 5

val sum = num1 + num2
val difference = num1 - num2
val product = num1 * num2

println(sum)        // 輸出:15
println(difference)  // 輸出:5
println(product)     // 輸出:50

在上述示例中,我們定義了兩個(gè)整數(shù)變量 num1 和 num2。然后,我們使用內(nèi)置的運(yùn)算符 +、- 和 * 來執(zhí)行加法、減法和乘法操作,并將結(jié)果分別賦值給 sum、difference 和 product。

7.傳名參數(shù)

傳名參數(shù)(Call-by-Name Parameters)是一種特殊的參數(shù)傳遞方式,它允許我們將表達(dá)式作為參數(shù)傳遞給函數(shù),并在需要時(shí)進(jìn)行求值。傳名參數(shù)使用 => 符號(hào)來定義,以表示傳遞的是一個(gè)表達(dá)式而不是具體的值。

傳名參數(shù)的特點(diǎn)是,在每次使用參數(shù)時(shí)都會(huì)重新求值表達(dá)式,而不是在調(diào)用函數(shù)時(shí)進(jìn)行求值。這樣可以延遲表達(dá)式的求值,只在需要時(shí)才進(jìn)行計(jì)算。傳名參數(shù)通常用于需要延遲計(jì)算、惰性求值或者需要按需執(zhí)行的場(chǎng)景。

下面是一個(gè)示例代碼:

def callByName(param: => Int): Unit = {
  println("Inside callByName")
  println("Param 1: " + param)
  println("Param 2: " + param)
}

def randomNumber(): Int = {
  println("Generating random number")
  scala.util.Random.nextInt(100)
}

callByName(randomNumber())

輸出:
Inside callByName
Generating random number
Param 1: 53
Generating random number
Param 2: 87

在上述示例中,定義了一個(gè)名為 callByName 的函數(shù),它接受一個(gè)傳名參數(shù) param。在函數(shù)體內(nèi),我們打印出兩次參數(shù)的值。

另外,定義了一個(gè)名為 randomNumber 的函數(shù),它用于生成隨機(jī)數(shù)。在該函數(shù)內(nèi)部,我們打印出生成隨機(jī)數(shù)的消息,并使用 scala.util.Random.nextInt 方法生成一個(gè)介于 0 到 100 之間的隨機(jī)數(shù)。

在主程序中,我們調(diào)用 callByName 函數(shù),并將 randomNumber() 作為傳名參數(shù)傳遞進(jìn)去。

當(dāng)程序執(zhí)行時(shí),會(huì)先打印出 "Inside callByName" 的消息,然后兩次調(diào)用 param,即 randomNumber()。在每次調(diào)用時(shí),都會(huì)重新生成一個(gè)新的隨機(jī)數(shù),并打印出相應(yīng)的值。

這說明傳名參數(shù)在每次使用時(shí)都會(huì)重新求值表達(dá)式,而不是在調(diào)用函數(shù)時(shí)進(jìn)行求值。這樣可以實(shí)現(xiàn)按需執(zhí)行和延遲計(jì)算的效果。

8.implicit

implicit 關(guān)鍵字用于定義隱式轉(zhuǎn)換和隱式參數(shù)。它可以用來簡化代碼,讓編譯器自動(dòng)執(zhí)行一些操作。

下面是一些使用 implicit 關(guān)鍵字的示例:

(1) 隱式轉(zhuǎn)換:可以使用 implicit 關(guān)鍵字定義隱式轉(zhuǎn)換函數(shù),讓編譯器自動(dòng)將一種類型的值轉(zhuǎn)換為另一種類型的值。

implicit def intToString(x: Int): String = x.toString

val x: String = 1
println(x) // 輸出 "1"ndom number
Param 2: 87

在這個(gè)例子中,定義了一個(gè)隱式轉(zhuǎn)換函數(shù) intToString,它接受一個(gè) Int 類型的參數(shù),并返回它的字符串表示。由于這個(gè)函數(shù)被定義為 implicit,因此編譯器會(huì)在需要時(shí)自動(dòng)調(diào)用它。

在主程序中,我們將一個(gè) Int 類型的值賦值給一個(gè) String 類型的變量。由于類型不匹配,編譯器會(huì)嘗試尋找一個(gè)隱式轉(zhuǎn)換函數(shù)來將 Int 類型的值轉(zhuǎn)換為 String 類型的值。在這個(gè)例子中,編譯器找到了我們定義的 intToString 函數(shù),并自動(dòng)調(diào)用它將 1 轉(zhuǎn)換為 "1"。

(2) 隱式參數(shù):可以使用 implicit 關(guān)鍵字定義隱式參數(shù),讓編譯器自動(dòng)為方法提供參數(shù)值。

implicit val x: Int = 1

def foo(implicit x: Int): Unit = println(x)

foo // 輸出 1

在這個(gè)例子中,定義了一個(gè)隱式值 x 并賦值為 1。然后我們定義了一個(gè)方法 foo,它接受一個(gè)隱式參數(shù) x。

在主程序中,我們調(diào)用了方法 foo,但沒有顯式地傳入?yún)?shù)。由于方法 foo 接受一個(gè)隱式參數(shù),因此編譯器會(huì)嘗試尋找一個(gè)隱式值來作為參數(shù)傳入。在這個(gè)例子中,編譯器找到了我們定義的隱式值 x 并將其作為參數(shù)傳入方法 foo。

9.Object & Class

在Scala中,class 和 object 都可以用來定義類型,但它們之間有一些重要的區(qū)別。class 定義了一個(gè)類,它可以被實(shí)例化。每次使用 new 關(guān)鍵字創(chuàng)建一個(gè)類的實(shí)例時(shí),都會(huì)創(chuàng)建一個(gè)新的對(duì)象。

class MyClass(x: Int) {
  def printX(): Unit = println(x)
}

val a = new MyClass(1)
val b = new MyClass(2)
a.printX() // 輸出 1
b.printX() // 輸出 2

構(gòu)造器可以通過提供一個(gè)默認(rèn)值來擁有可選參數(shù):

class Point(var x: Int = 0, var y: Int = 0)

val origin = new Point  // x and y are both set to 0
val point1 = new Point(1)
println(point1.x)  // prints 1

在這個(gè)版本的Point類中,x和y擁有默認(rèn)值0所以沒有必傳參數(shù)。然而,因?yàn)闃?gòu)造器是從左往右讀取參數(shù),所以如果僅僅要傳個(gè)y的值,你需要帶名傳參。

class Point(var x: Int = 0, var y: Int = 0)
val point2 = new Point(y=2)
println(point2.y)  // prints 2

而 object 定義了一個(gè)單例對(duì)象。它不能被實(shí)例化,也不需要使用 new 關(guān)鍵字創(chuàng)建。在程序中,一個(gè) object 只有一個(gè)實(shí)例。此外,object 中定義的成員都是靜態(tài)的,這意味著它們可以在不創(chuàng)建實(shí)例的情況下直接訪問。而 class 中定義的成員只能在創(chuàng)建實(shí)例后訪問。

object MyObject {
  val x = 1
  def printX(): Unit = println(x)
}

MyObject.printX() // 輸出 1

另外,在Scala中,如果一個(gè) object 的名稱與一個(gè) class 的名稱相同,那么這個(gè) object 被稱為這個(gè) class 的伴生對(duì)象。伴生對(duì)象和類可以相互訪問彼此的私有成員:

class MyClass(x: Int) {
  private val secret = 42
  def printCompanionSecret(): Unit = println(MyClass.companionSecret)
}

object MyClass {
  private val companionSecret = 24
  def printSecret(c: MyClass): Unit = println(c.secret)
}

val a = new MyClass(1)
a.printCompanionSecret() // 輸出 24
MyClass.printSecret(a) // 輸出 42

在這個(gè)例子中,定義了一個(gè)類 MyClass 和它的伴生對(duì)象 MyClass。類 MyClass 中定義了一個(gè)私有成員變量 secret 和一個(gè)方法 printCompanionSecret,用于打印伴生對(duì)象中的私有成員變量 companionSecret。而伴生對(duì)象 MyClass 中定義了一個(gè)私有成員變量 companionSecret 和一個(gè)方法 printSecret,用于打印類 MyClass 的實(shí)例中的私有成員變量 secret。

在主程序中,創(chuàng)建了一個(gè)類 MyClass 的實(shí)例 a,并調(diào)用了它的 printCompanionSecret 方法。然后我們調(diào)用了伴生對(duì)象 MyClass 的 printSecret 方法,并將實(shí)例 a 作為參數(shù)傳入。

這就是Scala中類和伴生對(duì)象之間互相訪問私有成員的基本用法。

10.樣例類

樣例類(case class)是一種特殊的類,**常用于描述不可變的值對(duì)象(Value Object) **。

它們非常適合用于不可變的數(shù)據(jù)。定義一個(gè)樣例類非常簡單,只需在類定義前加上case關(guān)鍵字即可。例如,下面是一個(gè)簡單的樣例類定義:

case class Person(var name: String, var age: Int)

創(chuàng)建樣例類的實(shí)例時(shí),不需要使用new關(guān)鍵字,直接使用類名即可。例如,下面是一個(gè)創(chuàng)建樣例類實(shí)例并修改其成員變量的示例:

object Test01 {
  case class Person(var name: String, var age: Int)

  def main(args: Array[String]): Unit = {
    val z = Person("張三", 20)
    z.age = 23
    println(s"z = $z")
  }
}

11._(下劃線)

在Scala中,下劃線 _ 是一個(gè)特殊的符號(hào),它可以用在許多不同的地方,具有不同的含義。

  • 作為通配符:下劃線可以用作通配符,表示匹配任意值。例如,在模式匹配中,可以使用下劃線來表示匹配任意值。
x match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
  • 作為忽略符:下劃線也可以用來忽略不需要的值。例如,在解構(gòu)賦值時(shí),可以使用下劃線來忽略不需要的值。
val (x, _, z) = (1, 2, 3)
  • 作為函數(shù)參數(shù)占位符:下劃線還可以用作函數(shù)參數(shù)的占位符,表示一個(gè)匿名函數(shù)的參數(shù)。例如,在調(diào)用高階函數(shù)時(shí),可以使用下劃線來簡化匿名函數(shù)的定義。
val list = List(1, 2, 3)
list.map(_ * 2)
  • 將方法轉(zhuǎn)換為函數(shù):在方法名稱后加一個(gè)下劃線,會(huì)將其轉(zhuǎn)化為偏應(yīng)用函數(shù)(partially applied function),就能直接賦值了。
def add(x: Int, y: Int) = x + y
val f = add _

這只是下劃線在Scala中的一些常見用法。由于下劃線在不同的上下文中具有不同的含義,因此在使用時(shí)需要根據(jù)具體情況進(jìn)行判斷。

12.println

println 函數(shù)用于向標(biāo)準(zhǔn)輸出打印一行文本。它可以接受多種不同類型的參數(shù),并將它們轉(zhuǎn)換為字符串進(jìn)行輸出。

下面是一些常見的使用 println 函數(shù)進(jìn)行輸出的方式:

  • 輸出字符串:直接將字符串作為參數(shù)傳入 println 函數(shù),它會(huì)將字符串原樣輸出。
println("Hello, world!")
  • 輸出變量:將變量作為參數(shù)傳入 println 函數(shù),它會(huì)將變量的值轉(zhuǎn)換為字符串并輸出。
val x = 1
println(x)
  • 輸出表達(dá)式:將表達(dá)式作為參數(shù)傳入 println 函數(shù),它會(huì)計(jì)算表達(dá)式的值并將其轉(zhuǎn)換為字符串輸出。
val x = 1
val y = 2
println(x + y)
  • 使用字符串插值:可以使用字符串插值來格式化輸出。在字符串前加上 s 前綴,然后在字符串中使用 ${expression} 的形式來插入表達(dá)式的值。
val name = "Alice"
val age = 18
println(s"My name is $name and I am $age years old.")

這些是 println 函數(shù)的一些常見用法。你可以根據(jù)需要使用不同的方式來格式化輸出。

13.集合

在Scala中,集合有三大類:序列Seq、集Set、映射Map,所有的集合都擴(kuò)展自Iterable,所以Scala中的集合都可以使用 foreach方法。在Scala中集合有可變(mutable)和不可變(immutable)兩種類型。

14.List

如我們可以使用如下方式定義一個(gè)List,其他集合類型的定義方式也差不多。

object Main {
  def main(args: Array[String]): Unit = {
    // 定義一個(gè)空的字符串列表
    var emptyList: List[String] = List()
    // 定義一個(gè)具有數(shù)據(jù)的列表
    var intList = List(1, 2, 3, 4, 5, 6)
    // 定義空列表
    var emptyList2 = Nil
    // 使用::運(yùn)算符連接元素
    var numList = 1 :: (2 :: (3 :: Nil))
    println(emptyList)
    println(intList)
    println(emptyList2)
    println(numList)
  }
}

輸出:
List()
List(1, 2, 3, 4, 5, 6)
List()
List(1, 2, 3)

下面是一些List的常用方法:

val list = List(1, 2, 3, 4)

// 獲取列表的長度
val length = list.length

// 獲取列表的第一個(gè)元素
val first = list.head

// 獲取列表的最后一個(gè)元素
val last = list.last

// 獲取列表除第一個(gè)元素外剩余的元素
val tail = list.tail

// 獲取列表除最后一個(gè)元素外剩余的元素
val init = list.init

// 反轉(zhuǎn)列表
val reversed = list.reverse

// 在列表頭部添加元素
val newList1 = 0 +: list

// 在列表尾部添加元素
val newList2 = list :+ 5

// 連接兩個(gè)列表
val list1 = List(1, 2)
val list2 = List(3, 4)
val concatenatedList = list1 ++ list2

// 檢查列表是否為空
val isEmpty = list.isEmpty

// 檢查列表是否包含某個(gè)元素
val containsElement = list.contains(1)

// 過濾列表中的元素
val filteredList = list.filter(_ > 2)

// 映射列表中的元素
val mappedList = list.map(_ * 2)

// 折疊列表中的元素(從左到右)
val sum1 = list.foldLeft(0)(_ + _)

// 折疊列表中的元素(從右到左)
val sum2 = list.foldRight(0)(_ + _)

// 拉鏈操作
val names = List("Alice", "Bob", "Charlie")
val ages = List(25, 32, 29)
val zipped = names.zip(ages) // List(("Alice", 25), ("Bob", 32), ("Charlie", 29))

// 拉鏈操作后解壓縮
val (unzippedNames, unzippedAges) = zipped.unzip // (List("Alice", "Bob", "Charlie"), List(25, 32, 29))

更多方法不再贅述,網(wǎng)上很容易查閱到相關(guān)文章。

15.Map

object Main {
  def main(args: Array[String]): Unit = {
    // 定義一個(gè)空的映射
    val emptyMap = Map()
    // 定義一個(gè)具有數(shù)據(jù)的映射
    val intMap = Map("key1" -> 1, "key2" -> 2)
    // 使用元組定義一個(gè)映射
    val tupleMap = Map(("key1", 1), ("key2", 2))
    println(emptyMap)
    println(intMap)
    println(tupleMap)
  }
}

輸出:
Map()
Map(key1 -> 1, key2 -> 2)
Map(key1 -> 1, key2 -> 2)

下面是map常用的一些方法:

val map = Map("key1" -> 1, "key2" -> 2)

// 獲取映射的大小
val size = map.size

// 獲取映射中的所有鍵
val keys = map.keys

// 獲取映射中的所有值
val values = map.values

// 檢查映射是否為空
val isEmpty = map.isEmpty

// 檢查映射是否包含某個(gè)鍵
val containsKey = map.contains("key1")

// 獲取映射中某個(gè)鍵對(duì)應(yīng)的值
val value = map("key1")

// 獲取映射中某個(gè)鍵對(duì)應(yīng)的值,如果不存在則返回默認(rèn)值
val valueOrDefault = map.getOrElse("key3", 0)

// 過濾映射中的元素
val filteredMap = map.filter { case (k, v) => v > 1 }

// 映射映射中的元素
val mappedMap = map.map { case (k, v) => (k, v * 2) }

// 遍歷映射中的元素
map.foreach { case (k, v) => println(s"key: $k, value: $v") }

這里的case關(guān)鍵字起到匹配的作用。

16.Range

Range屬于序列(Seq)這一類集合的子集。它表示一個(gè)整數(shù)序列,可以用來遍歷一個(gè)整數(shù)區(qū)間內(nèi)的所有整數(shù)。例如,1 to 5表示一個(gè)從1到5的整數(shù)序列,包括1和5。

Range常見于for循環(huán)中,如下可定義一個(gè)Range:

// 定義一個(gè)從1到5的整數(shù)序列,包括1和5
val range1 = 1 to 5

// 定義一個(gè)從1到5的整數(shù)序列,包括1但不包括5
val range2 = 1 until 5

// 定義一個(gè)從1到10的整數(shù)序列,步長為2
val range3 = 1 to 10 by 2

// 定義一個(gè)從10到1的整數(shù)序列,步長為-1
val range4 = 10 to 1 by -1

如果我們想把Range轉(zhuǎn)為List,我們可以這樣做:

val range = 1 to 5
val list = range.toList

Range繼承自Seq,因此它擁有Seq的所有常用方法,例如length、head、last、tail、init、reverse、isEmpty、contains、filter、map、foldLeft和foldRight等。它還擁有一些特殊的方法,例如:

val range = 1 to 10 by 2

// 獲取序列的起始值
val start = range.start

// 獲取序列的結(jié)束值
val end = range.end

// 獲取序列的步長
val step = range.step

// 獲取一個(gè)包括結(jié)束值的新序列
val inclusiveRange = range.inclusive

17.迭代器

迭代器(Iterator)是一種用于遍歷集合中元素的工具。它提供了一種方法來訪問集合中的元素,而不需要暴露集合的內(nèi)部結(jié)構(gòu)。在 Scala 中,你可以使用 iterator 方法來獲取一個(gè)集合的迭代器。

object Main {
  def main(args: Array[String]): Unit = {

    val list = List(1, 2, 3)
    val iterator = list.iterator

    // 1. 使用 hasNext 方法來檢查迭代器中是否還有元素
    val hasMoreElements = iterator.hasNext
    println(s"Has more elements: $hasMoreElements")

    // 2. 使用 next 方法來獲取迭代器中的下一個(gè)元素
    val nextElement = iterator.next()
    println(s"Next element: $nextElement")

    // 注意:上面的代碼已經(jīng)將迭代器移動(dòng)到了第二個(gè)元素,因此下面的代碼將從第二個(gè)元素開始執(zhí)行

    // 3. 使用 size 方法來獲取迭代器中元素的個(gè)數(shù)
    val size = iterator.size
    println(s"Size: $size")

    val size1 = iterator.size
    println(s"Size1: $size1")

    // 注意:上面的代碼已經(jīng)將迭代器移動(dòng)到了末尾,因此下面的代碼將不再有效

    // 4. 使用 contains 方法來檢查迭代器中是否包含某個(gè)元素
    val containsElement = iterator.contains(2)
    println(s"Contains element: $containsElement")
  }
}

輸出:
Has more elements: true
Next element: 1
Size: 2
Size1: 0
Contains element: false

特別注意:迭代器是一次性的,所以在使用完畢后就不能再次使用。因此,在上面的代碼中,我們?cè)谡{(diào)用 next 方法后就不能再使用其他方法來訪問迭代器中的元素了。所以 size1輸出為0。

18.Tuple

把Tuple從集合中抽出來講述是因?yàn)門uple不屬于集合。它是一種用來將多個(gè)值組合在一起的數(shù)據(jù)結(jié)構(gòu)。一個(gè)Tuple可以包含不同類型的元素,每個(gè)元素都有一個(gè)固定的位置。Scala 中的元組包含一系列類:Tuple2,Tuple3等,直到 Tuple22。

示例如下:

object Main {
  def main(args: Array[String]): Unit = {
    // 定義一個(gè)包含兩個(gè)元素的Tuple
    val tuple1 = (1, "hello")
    println(tuple1)

    // 定義一個(gè)包含三個(gè)元素的Tuple
    val tuple2 = (1, "hello", true)
    println(tuple2)

    // 定義一個(gè)包含多個(gè)不同類型元素的Tuple
    val tuple3 = (1, "hello", true, 3.14)
    println(tuple3)

    // 訪問Tuple中的元素
    val firstElement = tuple3._1
    val secondElement = tuple3._2
    println(s"first element: $firstElement, second element: $secondElement")
  }
}

輸出:
(1,hello)
(1,hello,true)
(1,hello,true,3.14)
first element: 1, second element: hello

下面是一些Tuple的常用方法:

object Main {
  def main(args: Array[String]): Unit = {
    val tuple = (1, "hello")
    // 交換二元組的元素
    // 輸出:(hello,1)
    val swapped = tuple.swap

    // 使用 copy 方法來創(chuàng)建一個(gè)新的 Tuple,其中某些元素被替換為新值
    //輸出:(1,world)
    val newTuple = tuple.copy(_2 = "world")

    // 遍歷元素
    // 輸出:1 hello
    tuple.productIterator.foreach(println)

    // 轉(zhuǎn)換為字符串
    // 輸出: (1,hello)
    val stringRepresentation = tuple.toString

    // 使用 Tuple.productArity 方法來獲取 Tuple 中元素的個(gè)數(shù)
    // 輸出:2
    val arity = tuple.productArity

    // 使用 Tuple.productElement 方法來訪問 Tuple 中的元素
    // 輸出:1
    val firstElement = tuple.productElement(0)
  }
}

19.提取器對(duì)象

提取器對(duì)象是一個(gè)包含有 unapply 方法的單例對(duì)象。apply 方法就像一個(gè)構(gòu)造器,接受參數(shù)然后創(chuàng)建一個(gè)實(shí)例對(duì)象,反之 unapply 方法接受一個(gè)實(shí)例對(duì)象然后返回最初創(chuàng)建它所用的參數(shù)。提取器常用在模式匹配和偏函數(shù)中。

下面是一個(gè)使用提取器對(duì)象(Extractor Object)的 Scala 代碼示例:

object Email {
  def apply(user: String, domain: String): String = s"$user@$domain"
  
  def unapply(email: String): Option[(String, String)] = {
    val parts = email.split("@")
    if (parts.length == 2) Some(parts(0), parts(1))
    else None
  }
}

// 測(cè)試
val address = "john.doe@example.com"
address match {
  case Email(user, domain) => println(s"User: $user, Domain: $domain")
  case _ => println("Invalid email address")
}

在上述示例中,定義了一個(gè)名為Email的提取器對(duì)象。提取器對(duì)象具有兩個(gè)方法:apply和unapply。

apply方法接收用戶名和域名作為參數(shù),并返回一個(gè)完整的電子郵件地址。在這個(gè)示例中,我們簡單地將用戶名和域名拼接成電子郵件地址的字符串。

unapply方法接收一個(gè)電子郵件地址作為參數(shù),并返回一個(gè)Option類型的元組。在這個(gè)示例中,我們使用split方法將電子郵件地址分割為用戶名和域名兩部分,并通過Some將它們封裝到一個(gè)Option中返回。如果分割后的部分不是兩部分,即電子郵件地址不符合預(yù)期的格式,我們返回None。

在測(cè)試部分,我們創(chuàng)建了一個(gè)電子郵件地址字符串a(chǎn)ddress。然后,我們使用match表達(dá)式將address與提取器對(duì)象Email進(jìn)行匹配。如果匹配成功,我們提取出用戶名和域名,并打印出對(duì)應(yīng)的信息。如果匹配失敗,即電子郵件地址無效,我們打印出相應(yīng)的錯(cuò)誤信息。

20.流程判斷

(1) while & if

object Main {
  def main(args: Array[String]): Unit = {
    println("----while----")
    var i = 0
    while (i < 5) {
      println(i)
      i += 1
    }

    println("----if----")
    val x = 3
    if (x > 0) {
      println("x大于0")
    } else {
      println("x小于0")
    }
  }
}

輸出:
----while----
0
1
2
3
4
----if----
x大于0

Scala中的while和if跟Java中的方法幾乎沒有區(qū)別。

(2) for

object Main {
  def main(args: Array[String]): Unit = {
  println("----for循環(huán)----")
    for (i <- 1 to 5) {
      println(i)
    }
  }
}

輸出:
----for循環(huán)----
1
2
3
4
5

for循環(huán)跟Java略微有點(diǎn)區(qū)別。其中i <- 1 to 5是Scala中for循環(huán)的一種常見形式。它表示遍歷一個(gè)序列,序列中的元素依次為1、2、3、4、5。

(3) 多重for循環(huán)簡寫

Scala中對(duì)于多重for循環(huán)可以進(jìn)行簡寫,例如我們要用Java寫多重for循環(huán)是下面這樣:

public class Main {
  public static void main(String[] args) {
    // 多重for循環(huán)
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        System.out.println(i + " " + j);
      }
    }
  }
}

而用Scala我們可以直接簡寫為下面這樣:

object Main {
  def main(args: Array[String]): Unit = {
    // 多重for循環(huán)
    for (i <- 0 until 3; j <- 0 until 3) {
      println(i + " " + j)
    }
  }
}

輸出:
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2

可以看出scala的for循環(huán)語法更加的精簡。代碼行數(shù)更少。

(4) yield

在for循環(huán)的過程中我們可以使用 yield來對(duì)for循環(huán)的元素進(jìn)行 操作收集:

object Main {
  def main(args: Array[String]): Unit = {
    val numbers = for (i <- 1 to 5) yield i * 2
    println(numbers)
  }
}

輸出:
Vector(2, 4, 6, 8, 10)

21.模式匹配(pattern matching)

在Scala語言中,沒有switch和case關(guān)鍵字。相反,我們可以使用模式匹配(pattern matching)來實(shí)現(xiàn)類似于switch語句的功能。它是Java中的switch語句的升級(jí)版,同樣可以用于替代一系列的 if/else 語句。下面是一個(gè)簡單的例子,它展示了如何使用模式匹配來實(shí)現(xiàn)類似于switch語句的功能:

object Main {
  def main(args: Array[String]): Unit = {
    def matchTest(x: Any): String = x match {
      case 1 => "one"
      case "two" => "two"
      case y: Int => "scala.Int"
      case _ => "many"
    }

    println(matchTest(1))
    println(matchTest("two"))
    println(matchTest(3))
    println(matchTest("test"))
  }
}

輸出:
one
two
scala.Int
many

在上面的例子中,定義了一個(gè)名為matchTest的函數(shù),它接受一個(gè)類型為Any的參數(shù)x。在函數(shù)體中,我們使用了一個(gè)模式匹配表達(dá)式來匹配參數(shù)x的值。

在模式匹配表達(dá)式中,我們定義了四個(gè)case子句。第一個(gè)case子句匹配值為1的情況;第二個(gè)case子句匹配值為"two"的情況;第三個(gè)case子句匹配類型為Int的情況;最后一個(gè)case子句匹配所有其他情況。

22.樣例類(case classes)的匹配

樣例類非常適合用于模式匹配。

abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class SMS(caller: String, message: String) extends Notification

def showNotification(notification: Notification): String = {
  notification match {
    case Email(sender, title, _) =>
      s"You got an email from $sender with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
  }
}

val someSms = SMS("12345", "Are you there?")
val someEmail = Email("John Doe", "Meeting", "Are we still meeting tomorrow?")

println(showNotification(someSms))
println(showNotification(someEmail))

這段代碼定義了一個(gè)抽象類 Notification,以及兩個(gè)擴(kuò)展自 Notification 的樣例類 Email 和 SMS。然后定義了一個(gè)函數(shù) showNotification,它接受一個(gè) Notification 類型的參數(shù),并使用模式匹配來檢查傳入的通知是 Email 還是 SMS,并相應(yīng)地生成一條消息。

最后,我們創(chuàng)建了兩個(gè)實(shí)例:一個(gè) SMS 和一個(gè) Email,并使用 showNotification 函數(shù)來顯示它們的消息。

23.模式守衛(wèi)(Pattern guards)

為了讓匹配更加具體,可以使用模式守衛(wèi),也就是在模式后面加上if <boolean expression>。

def checkNumberType(number: Int): String = number match {
  case n if n > 0 && n % 2 == 0 => "Positive even number"
  case n if n > 0 && n % 2 != 0 => "Positive odd number"
  case n if n < 0 && n % 2 == 0 => "Negative even number"
  case n if n < 0 && n % 2 != 0 => "Negative odd number"
  case _ => "Zero"
}

// 測(cè)試
println(checkNumberType(10))    // 輸出: Positive even number
println(checkNumberType(15))    // 輸出: Positive odd number
println(checkNumberType(-4))    // 輸出: Negative even number
println(checkNumberType(-9))    // 輸出: Negative odd number
println(checkNumberType(0))     // 輸出: Zero

在上述示例中,我們定義了一個(gè)名為checkNumberType的方法,它接收一個(gè)整數(shù)參數(shù)number并返回一個(gè)描述數(shù)字類型的字符串。

通過使用模式守衛(wèi),我們可以對(duì)number進(jìn)行多個(gè)條件的匹配,并根據(jù)條件來返回相應(yīng)的結(jié)果。在每個(gè)case語句中,我們使用模式守衛(wèi)來進(jìn)一步過濾匹配的數(shù)字。

例如,case n if n > 0 && n % 2 == 0 表示當(dāng) number 大于 0 且為偶數(shù)時(shí)執(zhí)行該分支。類似地,其他的 case 語句也使用了模式守衛(wèi)來進(jìn)行更精確的匹配。

在測(cè)試部分,我們調(diào)用了checkNumberType方法并傳入不同的整數(shù)進(jìn)行測(cè)試。根據(jù)不同的輸入,方法將返回相應(yīng)的字符串描述數(shù)字類型。

24.僅匹配類型

當(dāng)不同類型對(duì)象需要調(diào)用不同方法時(shí),僅匹配類型的模式非常有用

def processValue(value: Any): String = value match {
  case str: String => s"Received a String: $str"
  case num: Int => s"Received an Int: $num"
  case lst: List[_] => s"Received a List: $lst"
  case _: Double => "Received a Double"
  case _ => "Unknown value"
}

// 測(cè)試
println(processValue("Hello"))                // 輸出: Received a String: Hello
println(processValue(10))                     // 輸出: Received an Int: 10
println(processValue(List(1, 2, 3)))           // 輸出: Received a List: List(1, 2, 3)
println(processValue(3.14))                    // 輸出: Received a Double
println(processValue(true))                    // 輸出: Unknown value

在上述示例中,定義了一個(gè)名為processValue的方法,它接收一個(gè)任意類型的參數(shù)value,并返回一個(gè)描述值類型的字符串。

通過使用類型模式匹配,我們可以根據(jù)不同的值類型來執(zhí)行相應(yīng)的邏輯。在每個(gè)case語句中,我們使用類型模式匹配來匹配特定類型的值。

例如,case str: String 表示當(dāng) value 的類型為 String 時(shí)執(zhí)行該分支,并將其綁定到變量 str。類似地,其他的 case 語句也使用了類型模式匹配來匹配不同的值類型。

在測(cè)試部分,我們調(diào)用了processValue方法并傳入不同類型的值進(jìn)行測(cè)試。根據(jù)值的類型,方法將返回相應(yīng)的描述字符串。

Scala的模式匹配是我覺得非常實(shí)用和靈活的一個(gè)功能,比Java的switch語句更加強(qiáng)大和靈活。Scala的模式匹配可以匹配不同類型的值,包括數(shù)字、字符串、列表、元組等。而Java的switch語句只能匹配整數(shù)、枚舉和字符串類型的值。

25.密封類

特質(zhì)(trait)和類(class)可以用sealed標(biāo)記為密封的,這意味著其所有子類都必須與之定義在相同文件中,從而保證所有子類型都是已知的。密封類限制了可擴(kuò)展的子類類型,并在模式匹配中確保所有可能的類型都被處理,提高了代碼的安全性和可靠性。

下面是一個(gè)使用密封類(sealed class)和模式匹配的 Scala 代碼示例:

sealed abstract class Shape

case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
case class Square(side: Double) extends Shape

def calculateArea(shape: Shape): Double = shape match {
  case Circle(radius) => math.Pi * radius * radius
  case Rectangle(width, height) => width * height
  case Square(side) => side * side
}

// 測(cè)試
val circle = Circle(5.0)
val rectangle = Rectangle(3.0, 4.0)
val square = Square(2.5)

println(s"Area of circle: ${calculateArea(circle)}")         // 輸出: Area of circle: 78.53981633974483
println(s"Area of rectangle: ${calculateArea(rectangle)}")   // 輸出: Area of rectangle: 12.0
println(s"Area of square: ${calculateArea(square)}")         // 輸出: Area of square: 6.25

在上述示例中,我們定義了一個(gè)密封類Shape,它是一個(gè)抽象類,不能直接實(shí)例化。然后,我們通過擴(kuò)展Shape類創(chuàng)建了Circle、Rectangle和Square這三個(gè)子類。

在calculateArea方法中,我們使用模式匹配對(duì)傳入的shape進(jìn)行匹配,并根據(jù)不同的Shape子類執(zhí)行相應(yīng)的邏輯。在每個(gè)case語句中,我們根據(jù)具體的形狀類型提取相應(yīng)的屬性,并計(jì)算出面積。

在測(cè)試部分,我們創(chuàng)建了一個(gè)Circle對(duì)象、一個(gè)Rectangle對(duì)象和一個(gè)Square對(duì)象,并分別調(diào)用calculateArea方法計(jì)算它們的面積。

26.嵌套方法

當(dāng)在Scala中定義一個(gè)方法時(shí),我們可以選擇將其嵌套在另一個(gè)方法內(nèi)部。這樣的嵌套方法只在外部方法的作用域內(nèi)可見,而對(duì)于外部方法以外的代碼是不可見的。這可以幫助我們組織和封裝代碼,提高代碼的可讀性和可維護(hù)性。

def calculateDiscountedPrice(originalPrice: Double, discountPercentage: Double): Double = {
  def applyDiscount(price: Double, discount: Double): Double = {
    val discountedPrice = price - (price * discount)
    discountedPrice
  }

  def validateDiscount(discount: Double): Double = {
    val maxDiscount = 0.8 // 最大折扣為80%
    if (discount > maxDiscount) {
      maxDiscount
    } else {
      discount
    }
  }

  val validatedDiscount = validateDiscount(discountPercentage)
  val finalPrice = applyDiscount(originalPrice, validatedDiscount)
  finalPrice
}

// 調(diào)用外部方法
val price = calculateDiscountedPrice(100.0, 0.9)
println(s"The final price is: $price")

在上述示例中,定義了一個(gè)外部方法calculateDiscountedPrice,它接收原始價(jià)格originalPrice和折扣百分比discountPercentage作為參數(shù),并返回最終價(jià)格。

在calculateDiscountedPrice方法的內(nèi)部,我們定義了兩個(gè)嵌套方法:applyDiscount和validateDiscount。applyDiscount方法用于計(jì)算折扣后的價(jià)格,它接收價(jià)格和折扣作為參數(shù),并返回折扣后的價(jià)格。validateDiscount方法用于驗(yàn)證折扣百分比是否超過最大折扣限制,并返回一個(gè)有效的折扣百分比。

在外部方法中,我們首先調(diào)用validateDiscount方法來獲取有效的折扣百分比,然后將其與原始價(jià)格一起傳遞給applyDiscount方法,計(jì)算最終價(jià)格。最后,我們打印出最終價(jià)格。

27.正則表達(dá)式模型

正則表達(dá)式是用來找出數(shù)據(jù)中的指定模式(或缺少該模式)的字符串。.r方法可使任意字符串變成一個(gè)正則表達(dá)式。

object Main extends App {
  val emailPattern = "([a-zA-Z0-9_.+-]+)@([a-zA-Z0-9-]+)\\.([a-zA-Z0-9-.]+)".r

  def validateEmail(email: String): Boolean = email match {
    case emailPattern(username, domain, extension) =>
      println(s"Valid email address: $email")
      true
    case _ =>
      println(s"Invalid email address: $email")
      false
  }

  // 測(cè)試
  validateEmail("john.doe@example.com")        // 輸出: Valid email address: john.doe@example.com
  validateEmail("jane.doe@invalid")            // 輸出: Invalid email address: jane.doe@invalid
}

在上述示例中,我們首先創(chuàng)建了一個(gè)名為emailPattern的正則表達(dá)式對(duì)象,用于匹配電子郵件地址的模式。

然后,定義了一個(gè)名為validateEmail的方法,它接收一個(gè)字符串類型的電子郵件地址作為參數(shù),并使用正則表達(dá)式模式匹配來驗(yàn)證電子郵件地址的有效性。

在模式匹配的case語句中,我們使用emailPattern對(duì)傳入的電子郵件地址進(jìn)行匹配,并將匹配結(jié)果中的用戶名、域名和擴(kuò)展提取到相應(yīng)的變量中。如果匹配成功,我們打印出驗(yàn)證通過的消息,并返回true表示電子郵件地址有效。如果沒有匹配成功,則打印出驗(yàn)證失敗的消息,并返回false表示電子郵件地址無效。

在測(cè)試部分,我們調(diào)用validateEmail方法分別傳入一個(gè)有效的電子郵件地址和一個(gè)無效的電子郵件地址進(jìn)行測(cè)試。根據(jù)匹配結(jié)果,我們打印出相應(yīng)的驗(yàn)證消息。

28.型變

在 Scala 中,協(xié)變(covariance)和逆變(contravariance)是用來描述類型參數(shù)在子類型關(guān)系中的行為的概念。協(xié)變和逆變是用來指定泛型類型參數(shù)的子類型關(guān)系的方式,以確保類型安全性。

29.協(xié)變

協(xié)變(Covariance): 協(xié)變表示類型參數(shù)在子類型關(guān)系中具有相同的方向。如果一個(gè)泛型類的類型參數(shù)是協(xié)變的,那么子類型的關(guān)系將保持不變,即父類型可以被替換為子類型。在 Scala 中,可以使用 + 符號(hào)來表示協(xié)變。

下面是一個(gè)使用協(xié)變的示例代碼,使用 + 符號(hào)表示類型參數(shù) A 是協(xié)變的:

class Animal
class Dog extends Animal

class Cage[+A]

val dogCage: Cage[Dog] = new Cage[Dog]
val animalCage: Cage[Animal] = dogCage

在上述示例中,我們定義了一個(gè)協(xié)變類 Cage[+A],它接受一個(gè)類型參數(shù) A,并使用 + 符號(hào)來表示 A 是協(xié)變的。我們創(chuàng)建了一個(gè) dogCage,它是一個(gè) Cage[Dog] 類型的實(shí)例。然后,我們將 dogCage 賦值給一個(gè)類型為 Cage[Animal] 的變量 animalCage,這是合法的,因?yàn)?nbsp;Cage[+A] 的協(xié)變性允許我們將子類型的 Cage 賦值給父類型的 Cage。

30.逆變

逆變(Contravariance): 逆變表示類型參數(shù)在子類型關(guān)系中具有相反的方向。如果一個(gè)泛型類的類型參數(shù)是逆變的,那么子類型的關(guān)系將反轉(zhuǎn),即父類型可以替換為子類型。在 Scala 中,可以使用 - 符號(hào)來表示逆變。

下面是一個(gè)使用逆變的示例代碼,使用 - 符號(hào)表示類型參數(shù) A 是逆變的:

class Animal
class Dog extends Animal

class Cage[-A]

val animalCage: Cage[Animal] = new Cage[Animal]
val dogCage: Cage[Dog] = animalCage

在上述示例中,定義了一個(gè)逆變類 Cage[-A],它接受一個(gè)類型參數(shù) A,并使用 - 符號(hào)來表示 A 是逆變的。我們創(chuàng)建了一個(gè) animalCage,它是一個(gè) Cage[Animal] 類型的實(shí)例。然后,我們將 animalCage 賦值給一個(gè)類型為 Cage[Dog] 的變量 dogCage,這是合法的,因?yàn)?nbsp;Cage[-A] 的逆變性允許我們將父類型的 Cage 賦值給子類型的 Cage。通過協(xié)變和逆變,我們可以在 Scala 中實(shí)現(xiàn)更靈活的類型關(guān)系,并確保類型安全性。這在處理泛型集合或函數(shù)參數(shù)時(shí)特別有用。下面是一個(gè)更具體的示例:

abstract class Animal {
  def name: String
}

class Dog(val name: String) extends Animal {
  def bark(): Unit = println("Woof!")
}

class Cat(val name: String) extends Animal {
  def meow(): Unit = println("Meow!")
}

class Cage[+A](val animal: A) {
  def showAnimal(): Unit = println(animal.name)
}

def printAnimalNames(cage: Cage[Animal]): Unit = {
  cage.showAnimal()
}

val dog: Dog = new Dog("Fido")
val cat: Cat = new Cat("Whiskers")

val dogCage: Cage[Dog] = new Cage[Dog](dog)
val catCage: Cage[Cat] = new Cage[Cat](cat)

printAnimalNames(dogCage) // 輸出:Fido
printAnimalNames(catCage) // 輸出:Whiskers

在上述示例中,定義了一個(gè)抽象類 Animal,以及它的兩個(gè)子類 Dog 和 Cat。Dog 和 Cat 類都實(shí)現(xiàn)了 name 方法。

然后,定義了一個(gè)協(xié)變類 Cage[+A],它接受一個(gè)類型參數(shù) A,并使用協(xié)變符號(hào) + 表示 A 是協(xié)變的。Cage 類有一個(gè)名為 animal 的屬性,它的類型是 A,也就是動(dòng)物的類型。我們定義了一個(gè)名為 showAnimal() 的方法,它打印出 animal 的名稱。

接下來,定義了一個(gè)名為 printAnimalNames() 的函數(shù),它接受一個(gè)類型為 Cage[Animal] 的參數(shù),并打印出其中動(dòng)物的名稱。

我們創(chuàng)建了一個(gè) Dog 類型的對(duì)象 dog 和一個(gè) Cat 類型的對(duì)象 cat。然后,我們分別創(chuàng)建了一個(gè) Cage[Dog] 類型的 dogCage 和一個(gè) Cage[Cat] 類型的 catCage。

最后,我們分別調(diào)用 printAnimalNames() 函數(shù),并傳入 dogCage 和 catCage。由于 Cage 類是協(xié)變的,所以可以將 Cage[Dog] 和 Cage[Cat] 賦值給 Cage[Animal] 類型的參數(shù),而不會(huì)產(chǎn)生類型錯(cuò)誤。

31.類型限界

在 Scala 中,類型上界(Upper Bounds)和類型下界(Lower Bounds)是用于限制泛型類型參數(shù)的范圍的概念。它們?cè)试S我們?cè)诜盒皖惢蚍盒秃瘮?shù)中指定類型參數(shù)必須滿足某種條件。下面是關(guān)于類型上界和類型下界的解釋和示例代碼:

32.類型上界

類型上界(Upper Bounds): 類型上界用于指定泛型類型參數(shù)必須是某個(gè)類型或其子類型。我們使用 <: 符號(hào)來定義類型上界。例如,A <: B 表示類型參數(shù) A 必須是類型 B 或其子類型。

下面是一個(gè)使用類型上界的示例代碼:

abstract class Animal {
  def name: String
}

class Dog(val name: String) extends Animal {
  def bark(): Unit = println("Woof!")
}

class Cage[A <: Animal](val animal: A) {
  def showAnimal(): Unit = println(animal.name)
}

val dog: Dog = new Dog("Fido")
val cage: Cage[Animal] = new Cage[Dog](dog)

cage.showAnimal() // 輸出:Fido

在上述示例中,定義了一個(gè)抽象類 Animal,以及它的子類 Dog。Dog 類繼承自 Animal 類,并實(shí)現(xiàn)了 name 方法。

然后,定義了一個(gè)泛型類 Cage[A <: Animal],它接受一個(gè)類型參數(shù) A,并使用類型上界 A <: Animal 來確保 A 是 Animal 類型或其子類型。Cage 類有一個(gè)名為 animal 的屬性,它的類型是 A。我們定義了一個(gè)名為 showAnimal() 的方法,它打印出 animal 的名稱。

創(chuàng)建了一個(gè) Dog 類型的對(duì)象 dog。然后,我們創(chuàng)建了一個(gè) Cage[Animal] 類型的 cage,并將 dog 對(duì)象作為參數(shù)傳遞給它。

最后,調(diào)用 cage 的 showAnimal() 方法,它成功打印出了 Dog 對(duì)象的名稱。

33.類型下界

類型下界(Lower Bounds): 類型下界用于指定泛型類型參數(shù)必須是某個(gè)類型或其父類型。我們使用 > 符號(hào)來定義類型下界。例如,A >: B 表示類型參數(shù) A 必須是類型 B 或其父類型。

下面是一個(gè)使用類型下界的示例代碼:

class Animal {
  def sound(): Unit = println("Animal sound")
}

class Dog extends Animal {
  override def sound(): Unit = println("Dog barking")
}

class Cat extends Animal {
  override def sound(): Unit = println("Cat meowing")
}

def makeSound[A >: Dog](animal: A): Unit = {
  animal.sound()
}

val dog: Dog = new Dog
val cat: Cat = new Cat

makeSound(dog) // 輸出:Dog barking
makeSound(cat) // 輸出:Animal sound

在上述示例中,定義了一個(gè)基類 Animal,以及兩個(gè)子類 Dog 和 Cat。這些類都有一個(gè) sound() 方法,用于輸出不同的動(dòng)物聲音。

接下來,定義了一個(gè)泛型函數(shù) makeSound[A >: Dog](animal: A),其中類型參數(shù) A 的下界被定義為 Dog,即 A >: Dog。這意味著 A 必須是 Dog 類型或其父類型。

在 makeSound() 函數(shù)內(nèi)部,我們調(diào)用傳入的 animal 對(duì)象的 sound() 方法。

然后,創(chuàng)建了一個(gè) Dog 對(duì)象 dog 和一個(gè) Cat 對(duì)象 cat。

最后,分別調(diào)用 makeSound() 函數(shù),并將 dog 和 cat 作為參數(shù)傳遞進(jìn)去。由于類型下界被定義為 Dog,所以 dog 參數(shù)符合條件,而 cat 參數(shù)被隱式地向上轉(zhuǎn)型為 Animal,也滿足條件。因此,調(diào)用 makeSound() 函數(shù)時(shí),輸出了不同的聲音。

通過類型上界和類型下界,我們可以對(duì)泛型類型參數(shù)的范圍進(jìn)行限制,以確保類型的約束和類型安全性。這使得我們能夠編寫更靈活、可復(fù)用且類型安全的代碼。

34.內(nèi)部類

在 Scala 中,內(nèi)部類是一個(gè)定義在另一個(gè)類內(nèi)部的類。內(nèi)部類可以訪問外部類的成員,并具有更緊密的關(guān)聯(lián)性。下面是一個(gè)關(guān)于 Scala 中內(nèi)部類的解釋和示例代碼:

在 Scala 中,內(nèi)部類可以分為兩種類型:成員內(nèi)部類(Member Inner Class)和局部內(nèi)部類(Local Inner Class)。

成員內(nèi)部類:成員內(nèi)部類是定義在外部類的作用域內(nèi),并可以直接訪問外部類的成員(包括私有成員)。成員內(nèi)部類可以使用外部類的實(shí)例來創(chuàng)建和訪問。

下面是一個(gè)示例代碼:

class Outer {
  private val outerField: Int = 10

  class Inner {
    def printOuterField(): Unit = {
      println(s"Outer field value: $outerField")
    }
  }
}

val outer: Outer = new Outer
val inner: outer.Inner = new outer.Inner
inner.printOuterField() // 輸出:Outer field value: 10

在上述示例中,定義了一個(gè)外部類 Outer,它包含一個(gè)私有成員 outerField。內(nèi)部類 Inner 定義在 Outer 的作用域內(nèi),并可以訪問外部類的成員。

在主程序中,創(chuàng)建了外部類的實(shí)例 outer。然后,我們使用 outer.Inner 來創(chuàng)建內(nèi)部類的實(shí)例 inner。注意,我們需要使用外部類的實(shí)例來創(chuàng)建內(nèi)部類的實(shí)例。

最后,調(diào)用內(nèi)部類 inner 的 printOuterField() 方法,它成功訪問并打印了外部類的私有成員 outerField。

局部內(nèi)部類: 局部內(nèi)部類是定義在方法或代碼塊內(nèi)部的類。局部內(nèi)部類的作用域僅限于所在方法或代碼塊內(nèi)部,無法從外部訪問。

下面是一個(gè)示例代碼:

def outerMethod(): Unit = {
  val outerField: Int = 10

  class Inner {
    def printOuterField(): Unit = {
      println(s"Outer field value: $outerField")
    }
  }

  val inner: Inner = new Inner
  inner.printOuterField() // 輸出:Outer field value: 10
}

outerMethod()

在上述示例中,定義了一個(gè)外部方法 outerMethod。在方法內(nèi)部,我們定義了一個(gè)局部變量 outerField 和一個(gè)局部內(nèi)部類 Inner。

在方法內(nèi)部,創(chuàng)建了內(nèi)部類 Inner 的實(shí)例 inner。注意,內(nèi)部類的作用域僅限于方法內(nèi)部。

最后,調(diào)用內(nèi)部類 inner 的 printOuterField() 方法,它成功訪問并打印了外部變量 outerField。

通過使用內(nèi)部類,我們可以在 Scala 中實(shí)現(xiàn)更緊密的關(guān)聯(lián)性和封裝性,同時(shí)允許內(nèi)部類訪問外部類的成員。內(nèi)部類在某些場(chǎng)景下可以提供更清晰和組織良好的。

35.復(fù)合類型

在 Scala 中,復(fù)合類型(Compound Types)允許我們定義一個(gè)類型,它同時(shí)具有多個(gè)特質(zhì)(Traits)或類的特性。復(fù)合類型可以用于限制一個(gè)對(duì)象的類型,以便它同時(shí)具備多個(gè)特性。下面是關(guān)于復(fù)合類型的解釋和示例代碼:

復(fù)合類型使用 with 關(guān)鍵字將多個(gè)特質(zhì)或類組合在一起,形成一個(gè)新的類型。

下面是一個(gè)示例代碼:

trait Flyable {
  def fly(): Unit
}

trait Swimmable {
  def swim(): Unit
}

class Bird extends Flyable {
  override def fly(): Unit = println("Flying...")
}

class Fish extends Swimmable {
  override def swim(): Unit = println("Swimming...")
}

def action(obj: Flyable with Swimmable): Unit = {
  obj.fly()
  obj.swim()
}

val bird: Bird = new Bird
val fish: Fish = new Fish

action(bird) // 輸出:Flying...
action(fish) // 輸出:Swimming...

在上述示例中,定義了兩個(gè)特質(zhì) Flyable 和 Swimmable,分別表示可飛行和可游泳的特性。然后,我們定義了兩個(gè)類 Bird 和 Fish,分別實(shí)現(xiàn)了相應(yīng)的特質(zhì)。

接下來,定義了一個(gè)方法 action,它接受一個(gè)類型為 Flyable with Swimmable 的參數(shù)。這表示參數(shù)必須同時(shí)具備 Flyable 和 Swimmable 的特性。

在主程序中,創(chuàng)建了一個(gè) Bird 對(duì)象 bird 和一個(gè) Fish 對(duì)象 fish。

最后,分別調(diào)用 action 方法,并將 bird 和 fish 作為參數(shù)傳遞進(jìn)去。由于它們都同時(shí)具備 Flyable 和 Swimmable 的特性,所以可以成功調(diào)用 fly() 和 swim() 方法。

通過使用復(fù)合類型,可以在 Scala 中定義一個(gè)類型,它同時(shí)具備多個(gè)特質(zhì)或類的特性,從而實(shí)現(xiàn)更靈活和精確的類型約束。這有助于編寫更可靠和可復(fù)用的代碼。

36.多態(tài)方法

在 Scala 中,多態(tài)方法(Polymorphic Methods)允許我們定義可以接受多種類型參數(shù)的方法。這意味著同一個(gè)方法可以根據(jù)傳入?yún)?shù)的類型執(zhí)行不同的邏輯。下面是關(guān)于多態(tài)方法的解釋和示例代碼:

多態(tài)方法使用類型參數(shù)來定義方法的參數(shù)類型,并使用泛型來表示可以接受多種類型參數(shù)。在方法內(nèi)部,可以根據(jù)類型參數(shù)的實(shí)際類型執(zhí)行不同的邏輯。

下面是一個(gè)示例代碼:

def printType[T](value: T): Unit = {
  value match {
    case s: String => println("String: " + s)
    case i: Int => println("Int: " + i)
    case d: Double => println("Double: " + d)
    case _ => println("Unknown type")
  }
}

printType("Hello") // 輸出:String: Hello
printType(123) // 輸出:Int: 123
printType(3.14) // 輸出:Double: 3.14
printType(true) // 輸出:Unknown type

在上述示例中,定義了一個(gè)多態(tài)方法 printType,它接受一個(gè)類型參數(shù) T。根據(jù)傳入?yún)?shù)的類型,我們使用模式匹配來判斷其實(shí)際類型,并執(zhí)行相應(yīng)的邏輯。

在方法內(nèi)部,使用 match 表達(dá)式對(duì)傳入的參數(shù) value 進(jìn)行模式匹配。對(duì)于不同的類型,我們分別輸出相應(yīng)的類型信息。

在主程序中,多次調(diào)用 printType 方法,并傳入不同類型的參數(shù)。根據(jù)傳入的參數(shù)類型,方法會(huì)執(zhí)行相應(yīng)的邏輯并輸出對(duì)應(yīng)的類型信息。

37.函數(shù)

Scala中一個(gè)簡單的函數(shù)定義如下,我們可以在Scala中使用JDK的類:

import java.util.Date
object Main {
  def main(args: Array[String]): Unit = {
    printCurrentDate() // 輸出當(dāng)前日期和時(shí)間
  }
  def printCurrentDate(): Unit = {
    val currentDate = new Date()
    println(currentDate.toString)
  }
}

38.函數(shù)默認(rèn)值

在 Scala 中,可以為函數(shù)參數(shù)指定默認(rèn)值。這樣,當(dāng)調(diào)用函數(shù)時(shí)如果沒有提供參數(shù)值,將使用默認(rèn)值。下面是一個(gè)簡單的示例:

object Main {
  def main(args: Array[String]): Unit = {
    greet() // 輸出 "Hello, World!"
    greet("Alice") // 輸出 "Hello, Alice!"
  }

  def greet(name: String = "World"): Unit = {
    println(s"Hello, $name!")
  }
}

39.高階函數(shù)

高階函數(shù)是指使用其他函數(shù)作為參數(shù)、或者返回一個(gè)函數(shù)作為結(jié)果的函數(shù)。在Scala中函數(shù)是“一等公民”,所以允許定義高階函數(shù)。這里的術(shù)語可能有點(diǎn)讓人困惑,我們約定,使用函數(shù)值作為參數(shù),或者返回值為函數(shù)值的“函數(shù)”和“方法”,均稱之為“高階函數(shù)”。

def applyFuncToList(list: List[Int], f: Int => Int): List[Int] = {
  list.map(f)
}

val numbers = List(1, 2, 3, 4)
val double = (x: Int) => x * 2
val doubledNumbers = applyFuncToList(numbers, double) // List(2, 4, 6, 8)

在這個(gè)例子中,applyFuncToList 函數(shù)接受一個(gè)整數(shù)列表和一個(gè)函數(shù) f,該函數(shù)將一個(gè)整數(shù)作為輸入并返回一個(gè)整數(shù)。然后,applyFuncToList 函數(shù)使用 map 方法將函數(shù) f 應(yīng)用于列表中的每個(gè)元素。在上面的代碼中,我們定義了一個(gè) double 函數(shù),它將輸入乘以2,并將其傳遞給 applyFuncToList 函數(shù)以對(duì)數(shù)字列表中的每個(gè)元素進(jìn)行加倍。

40.匿名函數(shù)

在 Scala 中,匿名函數(shù)是一種沒有名稱的函數(shù),可以用來創(chuàng)建簡潔的函數(shù)字面量。它們通常用于傳遞給高階函數(shù),或作為局部函數(shù)使用。

例如,下面是一個(gè)簡單的匿名函數(shù),它接受兩個(gè)整數(shù)參數(shù)并返回它們的和:

object Main {
  def main(args: Array[String]): Unit = {
    val add = (x: Int, y: Int) => x + y
    println(add(1, 2))  //輸出:3
  }
}

41.偏應(yīng)用函數(shù)

簡單來說,偏應(yīng)用函數(shù)就是一種只對(duì)輸入值的某個(gè)子集進(jìn)行處理的函數(shù)。它只會(huì)對(duì)符合特定條件的輸入值進(jìn)行處理,而對(duì)于不符合條件的輸入值則會(huì)拋出異常。

舉個(gè)例子:

object Main {
  def main(args: Array[String]): Unit = {

    println(divide.isDefinedAt(0)) // false
    println(divideSafe.isDefinedAt(0)) // true

    println(divide(1)) // 42
    println(divideSafe(1)) // Some(42)

    // println(divide(0)) // 拋出異常
    println(divideSafe(0)) // None
  }

  val divide: PartialFunction[Int, Int] = {
    case d: Int if d != 0 => 42 / d
  }

  val divideSafe: PartialFunction[Int, Option[Int]] = {
    case d: Int if d != 0 => Some(42 / d)
    case _ => None
  }

}

這個(gè)例子中,divide 是一個(gè)偏應(yīng)用函數(shù),它只定義了對(duì)非零整數(shù)的除法運(yùn)算。如果我們嘗試用 divide 函數(shù)去除以零,它會(huì)拋出一個(gè)異常。其中isDefinedAt 是一個(gè)方法,它用于檢查偏應(yīng)用函數(shù)是否在給定的輸入值上定義。如果偏應(yīng)用函數(shù)在給定的輸入值上定義,那么 isDefinedAt 方法會(huì)返回 true,否則返回 false。

為了避免這種情況,我們可以使用 divideSafe 函數(shù),它返回一個(gè) Option 類型的結(jié)果。如果除數(shù)為零,它會(huì)返回 None 而不是拋出異常。

42.柯里化函數(shù)

柯里化(Currying)是一種將多參數(shù)函數(shù)轉(zhuǎn)換為一系列單參數(shù)函數(shù)的技術(shù)。我們可以使用柯里化來定義函數(shù),例如:

def add(a: Int)(b: Int): Int = a + b

這個(gè) add 函數(shù)接受兩個(gè)參數(shù) a 和 b,并返回它們的和。由于它是一個(gè)柯里化函數(shù),所以我們可以將它看作是一個(gè)接受單個(gè)參數(shù) a 的函數(shù),它返回一個(gè)接受單個(gè)參數(shù) b 的函數(shù)。

我們可以這樣調(diào)用這個(gè)函數(shù):

val result = add(1)(2) // 3

或者這樣:

val addOne = add(1) _
val result = addOne(2) // 3

在上面的例子中,我們首先調(diào)用 add 函數(shù)并傳入第一個(gè)參數(shù) 1,然后我們得到一個(gè)新的函數(shù) addOne,它接受一個(gè)參數(shù)并返回它與 1 的和。最后,我們調(diào)用 addOne 函數(shù)并傳入?yún)?shù) 2,得到結(jié)果 3。

柯里化函數(shù)可以幫助我們實(shí)現(xiàn)參數(shù)復(fù)用和延遲執(zhí)行等功能。

柯里化函數(shù)的好處之一是它可以讓我們給一個(gè)函數(shù)傳遞較少的參數(shù),得到一個(gè)已經(jīng)記住了某些固定參數(shù)的新函數(shù)。這樣,我們就可以在不同的地方使用這個(gè)新函數(shù),而不需要每次都傳遞相同的參數(shù)2。

此外,柯里化函數(shù)還可以幫助我們實(shí)現(xiàn)函數(shù)的延遲計(jì)算。當(dāng)我們傳遞部分參數(shù)時(shí),它會(huì)返回一個(gè)新的函數(shù),可以在新的函數(shù)中繼續(xù)傳遞后面的參數(shù)。這樣,我們就可以根據(jù)需要來決定何時(shí)執(zhí)行這個(gè)函數(shù)。

43.惰性函數(shù)

可以使用 lazy 關(guān)鍵字定義惰性函數(shù)。惰性函數(shù)的執(zhí)行會(huì)被推遲,直到我們首次對(duì)其取值時(shí)才會(huì)執(zhí)行。

下面是一個(gè)簡單的例子,展示了如何定義和使用惰性函數(shù):

def sum(x: Int, y: Int): Int = {
  println("sum函數(shù)被執(zhí)行了...")
  x + y
}

lazy val res: Int = sum(1, 2)

println("-----")
println(res)

在這個(gè)例子中,我們定義了一個(gè)函數(shù) sum,它接受兩個(gè)參數(shù)并返回它們的和。然后我們定義了一個(gè)惰性值 res 并將其賦值為 sum(1, 2)。

在主程序中,我們首先打印了一行分隔符。然后我們打印了變量 res 的值。由于 res 是一個(gè)惰性值,因此在打印它之前,函數(shù) sum 并沒有被執(zhí)行。只有當(dāng)我們首次對(duì) res 取值時(shí),函數(shù) sum 才會(huì)被執(zhí)行。

這就是Scala中惰性函數(shù)的基本用法。你可以使用 lazy 關(guān)鍵字定義惰性函數(shù),讓函數(shù)的執(zhí)行被推遲。

總結(jié)

在總結(jié)之處,我希望強(qiáng)調(diào)Scala的美學(xué)和實(shí)用性。它是一種同時(shí)支持函數(shù)式編程和面向?qū)ο缶幊痰恼Z言,Scala的語法設(shè)計(jì)使其對(duì)初學(xué)者非常友好,同時(shí)也為更深入地探索編程提供了空間。

學(xué)習(xí)Scala不僅能夠幫助你提高編程效率,還能開闊你的編程視野。當(dāng)你熟練掌握Scala后,你將發(fā)現(xiàn)一個(gè)全新的、充滿無限可能的編程世界正在向你敞開。今天,我們只是輕輕掀開了Scala的神秘面紗,未來等待你去挖掘的還有更多。

請(qǐng)繼續(xù)探索和嘗試,讓自己真正理解并掌握Scala的精髓。持續(xù)學(xué)習(xí),不斷思考,享受編程的樂趣。

最后,希望這篇文章能給你帶來收獲和思考。

責(zé)任編輯:趙寧寧 來源: Java隨想錄
相關(guān)推薦

2018-10-28 16:14:55

Reactreact.js前端

2020-08-16 13:10:46

TensorFlow深度學(xué)習(xí)數(shù)據(jù)集

2022-04-24 15:21:01

MarkdownHTML

2023-02-19 15:31:09

架構(gòu)軟件開發(fā)代碼

2022-10-10 15:28:45

負(fù)載均衡

2010-06-13 11:13:38

UML初學(xué)者指南

2022-07-22 13:14:57

TypeScript指南

2021-05-10 08:50:32

網(wǎng)絡(luò)管理網(wǎng)絡(luò)網(wǎng)絡(luò)性能

2022-03-28 09:52:42

JavaScript語言

2023-07-28 07:31:52

JavaScriptasyncawait

2023-07-03 15:05:07

預(yù)測(cè)分析大數(shù)據(jù)

2009-07-08 09:32:40

ScalaScala與Java

2023-11-08 14:27:31

計(jì)算機(jī)視覺人工智能

2023-10-16 07:04:03

2010-08-26 15:47:09

vsftpd安裝

2011-08-10 19:16:50

Objective-C變量

2012-03-14 10:56:23

web app

2023-02-10 08:37:28

2022-09-05 15:36:39

Linux日志記錄syslogd

2016-12-30 13:23:30

AI 初學(xué)者分類
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)