Scala與Java差異(三)之函式

茅坤寶駿氹發表於2020-11-22

一、函式定義

(1)函式的定義與呼叫

在Scala中定義函式時,需要定義函式的函式名、引數、函式體。

第一個函式如下所示:

def sayHello(name: String, age: Int) = {

  if (age > 18) { printf("hi %s, you are a big boy\n", name); age }

  else { printf("hi %s, you are a little boy\n", name); age

}

sayHello("leo", 30)

Scala要求必須給出所有引數的型別,但是不一定給出函式返回值的型別,只要右側的函式體中不包含遞迴的語句,Scala就可以自己根據右側的表示式推斷出返回型別。

 (2)在程式碼塊中定義包含多行語句的函式體

單行的函式:def sayHello(name: String) = print("Hello, " + name)

如果函式體中有多行程式碼,則可以使用程式碼塊的方式包裹多行程式碼,程式碼塊中最後一行的返回值就是整個函式的返回值。與Java中不同,不是使用return返回值的。

比如如下的函式,實現累加的功能:

def sum(n: Int) = {

  var sum = 0;

  for(i <- 1 to n) sum += i

  sum
}

(3)遞迴函式與返回型別

如果在函式體內遞迴呼叫函式自身,則必須手動給出函式的返回型別。

例如,實現經典的斐波那契數列:

9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4; ....

def fab(n: Int): Int = {

  if(n <= 1) 1

  else fab(n - 1) + fab(n - 2)

}

 

二、函式的預設引數和帶名引數

(1) 預設引數

在Scala中,有時我們呼叫某些函式時,不希望給出引數的具體值,而希望使用引數自身預設的值,此時就定義在定義函式時使用預設引數。

def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName 

如果給出的引數不夠,則會從作往右依次應用引數。

(2)Java與Scala實現預設引數的區別

Java:

public void sayHello(String name, int age) {

  if(name == null) {

    name = "defaultName"

  }

  if(age == 0) {

    age = 18

  }

}

sayHello(null, 0)

Scala:

def sayHello(name: String, age: Int = 20) {

  print("Hello, " + name + ", your age is " + age)

}

sayHello("leo")

(3)帶名引數

在呼叫函式時,也可以不按照函式定義的引數順序來傳遞引數,而是使用帶名引數的方式來傳遞。

sayHello(firstName = "Mick", lastName = "Nina", middleName = "Jack") 

還可以混合使用未命名引數和帶名引數,但是未命名引數必須排在帶名引數前面。

sayHello("Mick", lastName = "Nina", middleName = "Jack")

 

三、函式的變長引數

(1)變長引數

在Scala中,有時我們需要將函式定義為引數個數可變的形式,則此時可以使用變長引數定義函式。

def sum(nums: Int*) = {

  var res = 0

  for (num <- nums) res += num

  res

}

sum(1, 2, 3, 4, 5)

(2)使用序列呼叫變長引數

在如果想要將一個已有的序列直接呼叫變長引數函式,是不對的。比如val s = sum(1 to 5)。此時需要使用Scala特殊的語法將引數定義為序列,讓Scala直譯器能夠識別。這種語法非常有用!一定要好好注意,在spark的原始碼中大量地使用到了。

val s = sum(1 to 5: _*)

案例:使用遞迴函式實現累加

def sum2(nums: Int*): Int = {

  if (nums.length == 0) 0

  else nums.head + sum2(nums.tail: _*)

 

四、函式的過程、lazy值和異常

(1)過程

在Scala中,定義函式時,如果函式體直接包裹在了花括號裡面,而沒有使用=連線,則函式的返回值型別就是Unit。這樣的函式就被稱之為過程。過程通常用於不需要返回值的函式。

過程還有一種寫法,就是將函式的返回值型別定義為Unit。

def sayHello(name: String) = "Hello, " + name

def sayHello(name: String) { print("Hello, " + name); "Hello, " + name }

def sayHello(name: String): Unit = "Hello, " + name

(2)lazy值

在Scala中,提供了lazy值的特性,也就是說,如果將一個變數宣告為lazy,則只有在第一次使用該變數時,變數對應的表示式才會發生計算。這種特性對於特別耗時的計算操作特別有用,比如開啟檔案進行IO,進行網路IO等。

import scala.io.Source._

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

即使檔案不存在,也不會報錯,只有第一個使用變數時會報錯,證明了表示式計算的lazy特性。

val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

def lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

(3) 異常

在Scala中,異常處理和捕獲機制與Java是非常相似的。

try {

  throw new IllegalArgumentException("x should not be negative")

} catch {

  case _: IllegalArgumentException => println("Illegal Argument!")

} finally {

  print("release resources!")

}
try {

  throw new IOException("user defined exception")

} catch {

  case e1: IllegalArgumentException => println("illegal argument")

  case e2: IOException => println("io exception")

}

 

相關文章