Scala_05_方法和函式
章節目標
- 掌握方法的格式和用法
- 掌握函式的格式和用法
- 掌握九九乘法表案例
1. 方法
1.1 概述
實際開發中, 我們需要編寫大量的邏輯程式碼, 這就勢必會涉及到重複的需求. 例如: 求10和20的最大值, 求11和22的最大值, 像這樣的需求, 用來進行比較的邏輯程式碼需要編寫兩次
, 而如果把比較的邏輯程式碼放到方法中, 只需要編寫一次就可以了, 這就是方法. scala中的方法和Java方法類似, 但scala與Java定義方法的語法是不一樣的。
1.2 語法格式
def 方法名(引數名:引數型別, 引數名:引數型別) : [return type] = {
//方法體
}
注意:
- 引數列表的引數型別不能省略
- 返回值型別可以省略,由scala編譯器自動推斷
- 返回值可以不寫return,預設就是{}塊表示式的值
1.3 示例
**需求: **
- 定義一個方法getMax,用來獲取兩個整型數字的最大值, 並返回結果(最大值).
- 呼叫該方法獲取最大值, 並將結果列印到控制檯上.
參考程式碼
- 方式一: 標準寫法
//1. 定義方法, 用來獲取兩個整數的最大值. def getMax(a:Int, b:Int): Int = { return if(a > b) a else b } //2. 呼叫方法, 獲取最大值. val max = getMax(10, 20) //3. 列印結果. println("max: " + max)
- 方式二: 優化版
//1. 定義方法, 用來獲取兩個整數的最大值.
def getMax(a:Int, b:Int) = if(a > b) a else b
//2. 呼叫方法, 獲取最大值.
val max = getMax(22, 11)
//3. 列印結果.
println("max: " + max)
1.4 返回值型別推斷
scala定義方法可以省略返回值的資料型別,由scala自動推斷返回值型別。這樣方法定義後更加簡潔。
注意: 定義遞迴方法,不能省略返回值型別
示例
定義遞迴方法, 求5的階乘.
步驟
-
定義方法factorial, 用來計算某個數字的階乘
規律: 1的階乘等於1, 其他數字的階乘為: n! = n * (n - 1)!
-
呼叫方法, 獲取5的階乘, 並將結果列印到控制檯上.
參考程式碼
//1. 定義方法factorial, 用來計算某個數字的階乘
def factorial(n:Int):Int = if(n == 1) 1 else n * factorial(n - 1)
//2. 呼叫方法, 獲取5的階乘.
val result = factorial(5)
//3. 將結果列印到控制檯上.
println("result: " + result)
1.5 惰性方法
當記錄方法返回值的變數被宣告為lazy時, 方法的執行將被推遲, 直到我們首次使用該值時, 方法才會執行, 像這樣的方法, 就叫: 惰性方法.
注意:
- Java中並沒有提供原生態的"惰性"技術, 但是可以通過特定的程式碼結構實現, 這種結構被稱之為: 懶載入(也叫延遲載入)
- lazy不能修飾var型別的變數.
使用場景:
-
開啟資料庫連線
由於表示式執行代價昂貴, 因此我們希望能推遲該操作, 直到我們確實需要表示式結果值時才執行它
-
提升某些特定模組的啟動時間.
為了縮短模組的啟動時間, 可以將當前不需要的某些工作推遲執行
-
確保物件中的某些欄位能優先初始化
為了確保物件中的某些欄位能優先初始化, 我們需要對其他欄位進行惰性化處理
需求
定義一個方法用來獲取兩個整數和, 通過"惰性"技術呼叫該方法, 然後列印結果.
參考程式碼
//1. 定義方法, 用來獲取兩個整數和
def getSum(a:Int, b:Int) = {
println("getSum方法被執行了...")
a + b
}
//2. 通過"惰性"方式呼叫該方法.
lazy val sum = getSum(1, 2) //此時我們發現getSum方法並沒有執行, 說明它的執行被推遲了.
//3. 列印結果, 並觀察
println("sum: " + sum) //列印結果為sum: 3, 說明首次使用方法返回值時, 方法才會載入執行.
1.6 方法引數
scala中的方法引數,使用比較靈活。它支援以下幾種型別的引數:
- 預設引數
- 帶名引數
- 變長引數
1.6.1 預設引數
在定義方法時可以給引數定義一個預設值。
示例
- 定義一個計算兩個整數和的方法,這兩個值分別預設為10和20
- 呼叫該方法,不傳任何引數
參考程式碼
//1. 定義一個方法, 用來獲取兩個整數的和
// x,y的預設值分別為10和20
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通過預設引數的形式, 呼叫方法
val sum = getSum()
//3. 列印結果
println("sum: " + sum)
1.6.2 帶名引數
在呼叫方法時,可以指定引數的名稱來進行呼叫。
示例
- 定義一個計算兩個整數和的方法,這兩個值分別預設為10和20
- 呼叫該方法,只設置第一個引數的值
參考程式碼
//1. 定義一個方法, 用來獲取兩個整數的和
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通過預設引數的形式, 呼叫方法
val sum = getSum(x=1)
//3. 列印結果
println("sum: " + sum)
1.6.3 變長引數
如果方法的引數是不固定的,可以將該方法的引數定義成變長引數。
語法格式:
def 方法名(引數名:引數型別*):返回值型別 = {
//方法體
}
注意:
- 在引數型別後面加一個
*
號,表示引數可以是0個或者多個- 一個方法有且只能有一個變長引數, 並且變長引數要放到引數列表的最後邊.
**示例一: **
- 定義一個計算若干個值相加的方法
- 呼叫方法,傳入以下資料:1,2,3,4,5
參考程式碼
//1. 定義一個計算若干個值相加的方法
def getSum(a:Int*) = a.sum
//2. 呼叫方法,傳入一些整數, 並獲取它們的和
val sum = getSum(1,2,3,4,5)
//3. 列印結果
println("sum: " + sum)
1.7 方法呼叫方式
在scala中,有以下幾種方法呼叫方式:
- 字尾呼叫法
- 中綴呼叫法
- 花括號呼叫法
- 無括號呼叫法
注意: 在編寫spark、flink程式時,會經常使用到這些方法呼叫方式。
1.7.1 字尾呼叫法
這種方法與Java沒有區別, 非常簡單.
語法
物件名.方法名(引數)
示例
使用字尾法呼叫Math.abs
, 用來求絕對值
參考程式碼
//字尾呼叫法
Math.abs(-1) //結果為1
1.7.2 中綴呼叫法
語法
物件名 方法名 引數
例如:1 to 10
注意: 如果有多個引數,使用括號括起來
示例
使用中綴法呼叫Math.abs
, 用來求絕對值
//中綴呼叫法
Math abs -1 //結果為1
擴充套件: 操作符即方法
來看一個表示式, 大家覺得這個表示式像不像方法呼叫?
1 + 1
在scala中,+ - * / %等這些操作符和Java一樣,但在scala中,
- 所有的操作符都是方法
- 操作符是一個方法名字是符號的方法
1.7.3 花括號呼叫法
語法
Math.abs{
// 表示式1
// 表示式2
}
注意: 方法只有一個引數,才能使用花括號呼叫法
示例
使用花括號呼叫法Math.abs
求絕對值
參考程式碼
//花括號呼叫法
Math.abs{-10} //結果為: 10
1.7.4 無括號呼叫法
如果方法沒有引數,可以省略方法名後面的括號
示例
- 定義一個無引數的方法,列印"Hello, Scala!"
- 使用無括號呼叫法呼叫該方法
參考程式碼
//1. 定義一個無引數的方法,列印"Hello, Scala!"
def sayHello() = println("Hello, Scala!")
//2. 呼叫方法
sayHello
注意:
- 在Scala中, 如果方法的返回值型別是Unit型別, 這樣的方法稱之為過程(procedure)
- 過程的等號(=)可以省略不寫. 例如:
def sayHello() = println("Hello, Scala!") //可以改寫為 def sayHello() { println("Hello, Scala!") } //注意: 這個花括號{}不能省略
2. 函式
scala支援函數語言程式設計,將來編寫Spark/Flink程式會大量使用到函式, 目前, 我們先對函式做一個簡單入門, 在後續的學習過程中, 我們會逐步重點講解函式的用法.
2.1 定義函式
語法
val 函式變數名 = (引數名:引數型別, 引數名:引數型別....) => 函式體
注意:
- 在Scala中, 函式是一個物件(變數)
- 類似於方法,函式也有引數列表和返回值
- 函式定義不需要使用
def
定義- 無需指定返回值型別
2.2 示例
rgb(251, 249, 253)
**需求: **
- 定義一個計算兩個整數和的函式
- 呼叫該函式
參考程式碼
//1. 定義一個用來計算兩個整數和的函式, 並將其賦值給變數sum
val getSum = (x:Int, y:Int) => x + y
//2. 呼叫函式.
val result = getSum(1,2)
//3. 列印結果
println("result: " + result)
2.3 方法和函式的區別
在Java中, 方法和函式之間沒有任何區別, 只是叫法不同. 但是在Scala中, 函式和方法就有區別了, 具體如下:
- 方法是隸屬於類或者物件的,在執行時,它是載入到JVM的方法區中
- 可以將函式物件賦值給一個變數,在執行時,它是載入到JVM的堆記憶體中
- 函式是一個物件,繼承自FunctionN,函式物件有apply,curried,toString,tupled這些方法。方法則沒有
結論: 在Scala中, 函式是物件, 而方法是屬於物件的, 所以可以理解為: 方法歸屬於函式.
示例
演示方法無法賦值給變數
//1. 定義方法
def add(x:Int,y:Int)= x + y
//2. 嘗試將方法賦值給變數.
//val a = add(1, 2) //不要這樣寫, 這樣寫是在"呼叫方法", 而不是把方法賦值給變數
val a = add
//3. 上述程式碼會報錯
<console>:12: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
val a = add
2.4 方法轉換為函式
有時候需要將方法轉換為函式. 例如: 作為變數傳遞,就需要將方法轉換為函式
格式
val 變數名 = 方法名 _ //格式為: 方法名 + 空格 + 下劃線
注意: 使用
_
即可將方法轉換為函式
示例
- 定義一個方法用來計算兩個整數和
- 將該方法轉換為一個函式,並賦值給變數
參考程式碼
//1. 定義一個方法用來計算兩個整數和
def add(x:Int, y:Int)= x + y
//2. 將該方法轉換為一個函式,並賦值給變數
val a = add _
//3. 呼叫函式, 用來獲取兩個整數的和.
val result = a(1, 2)
//4. 列印結果
println("result: " + result)
3. 案例: 列印nn乘法表
3.1 需求
定義方法實現, 根據使用者錄入的整數, 列印對應的乘法表。
例如: 使用者錄入5,則列印55乘法表,使用者錄入9,則列印99乘法表。
3.2 目的
- 考察
鍵盤錄入和方法, 函式
的綜合運用. - 體會方法和函式的不同.
3.3 步驟
- 定義方法(或者函式), 接收一個整型引數.
- 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
- 呼叫方法(函式), 輸出結果.
3.4 參考程式碼
- 方式一: 通過方法實現
//1. 定義一個方法, 接收一個整型引數.
def printMT(n:Int) = { //Multiplication Table(乘法表)
//2. 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t");
if(j==i) println()
}
}
//3. 呼叫方法
printMT(5)
//優化版: 上述定義方法的程式碼可以合併為一行(目前只要能看懂即可)
/*def printMT(n:Int) = for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/
- 方式二: 通過函式實現
//1. 定義一個函式, 接收一個整型引數.
val printMT = (n:Int) => {
//2. 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
for(i <- 1 to n; j <- 1 to i) {
print(s"${j} * ${i} = ${j * i}\t");
if(j==i) println()
}
}
//3. 呼叫函式
printMT(9)
//優化版: 上述定義函式的程式碼可以合併為一行(目前只要能看懂即可)
/*val printMT = (n:Int) => for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/