scala快速入門--Option類型、偏函數、正則表達式、異常處理、提取器、泛型的使用方法

scala快速入門–Option類型、偏函數、正則表達式、異常處理、提取器的使用方法

在這裏插入圖片描述

1、Option類型

使用Option類型,可以用來有效避免空引用(null)異常。也就是說,將來我們返回某些數據時,可以返回一個Option類型來替代。

scala中,Option類型來表示可選值。這種類型的數據有兩種形式:

  • Some(x):表示實際的值
    在這裏插入圖片描述
  • None:表示沒有值
    在這裏插入圖片描述
  • 使用getOrElse方法,當值爲None是可以指定一個默認值

示例一
示例說明

  • 定義一個兩個數相除的方法,使用Option類型來封裝結果
  • 然後使用模式匹配來打印結果
    • 不是除零,打印結果
    • 除零打印異常錯誤

參考代碼

  /**
    * 定義除法操作
    * @param a 參數1
    * @param b 參數2
    * @return Option包裝Double類型
    */
  def dvi(a:Double, b:Double):Option[Double] = {
    if(b != 0) {
      Some(a / b)
    }
    else {
      None
    }
  }

  def main(args: Array[String]): Unit = {
    val result1 = dvi(1.0, 5)

    result1 match {
      case Some(x) => println(x)
      case None => println("除零異常")
    }
  }

示例二
示例說明

  • 重寫上述案例,使用getOrElse方法,當除零時,或者默認值爲0

參考代碼

def dvi(a:Double, b:Double) = {
    if(b != 0) {
        Some(a / b)
    }
    else {
        None
    }
}

def main(args: Array[String]): Unit = {
    val result = dvi(1, 0).getOrElse(0)

    println(result)
}

2、偏函數

偏函數可以提供了簡潔的語法,可以簡化函數的定義。配合集合的函數式編程,可以讓代碼更加優雅。

定義

  • 偏函數被包在花括號內沒有match的一組case語句是一個偏函數

  • 偏函數是PartialFunction[A, B]的一個實例

    • A代表輸入參數類型
    • B代表返回結果類型

示例一
示例說明

定義一個偏函數,根據以下方式返回

輸入 返回值
1
2
3
其他 其他

參考代碼

// func1是一個輸入參數爲Int類型,返回值爲String類型的偏函數
val func1: PartialFunction[Int, String] = {
    case 1 => "一"
    case 2 => "二"
    case 3 => "三"
    case _ => "其他"
}

println(func1(2))

示例二
示例說明

  • 定義一個列表,包含1-10的數字

  • 請將1-3的數字都轉換爲[1-3]

  • 請將4-8的數字都轉換爲[4-8]

  • 將其他的數字轉換爲(8-*]

參考代碼

val list = (1 to 10).toList

val list2 = list.map{
    case x if x >= 1 && x <= 3 => "[1-3]"
    case x if x >= 4 && x <= 8 => "[4-8]"
    case x if x > 8 => "(8-*]"
}

println(list2)

3、正則表達式

在scala中,可以很方便地使用正則表達式來匹配數據。
定義

Regex類

  • scala中提供了Regex類來定義正則表達式

  • 要構造一個RegEx對象,直接使用String類的r方法即可

  • 建議使用三個雙引號來表示正則表達式,不然就得對正則中的反斜槓來進行轉義

    val regEx = """正則表達式""".r
    

findAllMatchIn方法

  • 使用findAllMatchIn方法可以獲取到所有正則匹配到的字符串

示例一
示例說明

參考代碼

val r = """.+@.+\..+""".r

val eml1 = "[email protected]"
val eml2 = "[email protected]"

if(r.findAllMatchIn(eml1).size > 0) {
    println(eml1 + "郵箱合法")
}
else {
    println(eml1 + "郵箱不合法")
}

if(r.findAllMatchIn(eml2).size > 0) {
    println(eml2 + "郵箱合法")
}
else {
    println(eml2 + "郵箱不合法")
}

示例二
示例說明

找出以下列表中的所有不合法的郵箱

"[email protected]", "[email protected]", "[email protected]", "123afadff.com"

參考代碼**

val emlList =
List("[email protected]", "[email protected]", "[email protected]", "123afadff.com")

val regex = """.+@.+\..+""".r

val invalidEmlList = emlList.filter {
    x =>
    if (regex.findAllMatchIn(x).size < 1) true else false
}

println(invalidEmlList)

示例三
示例說明

參考代碼

// 使用括號表示一個分組
val regex = """.+@(.+)\..+""".r

val emlList =
List("[email protected]", "[email protected]", "[email protected]", "123afadff.com")

val emlCmpList = emlList.map {
    case x@regex(company) => s"${x} => ${company}"
    case x => x + "=>未知"
}

println(emlCmpList)

4、異常處理

來看看下面一段代碼。

  def main(args: Array[String]): Unit = {
   val i = 10 / 0
    
    println("你好!")
  }

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at ForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala)

執行程序,可以看到scala拋出了異常,而且沒有打印出來"你好"。說明程序出現錯誤後就終止了。

那怎麼解決該問題呢?

在scala中,可以使用異常處理來解決這個問題

4.1、捕獲異常

語法格式

try {
    // 代碼
}
catch {
    case ex:異常類型1 => // 代碼
    case ex:異常類型2 => // 代碼
}
finally {
    // 代碼
}
  • try中的代碼是我們編寫的業務處理代碼
  • 在catch中表示當出現某個異常時,需要執行的代碼
  • 在finally中,是不管是否出現異常都會執行的代碼

示例
示例說明

  • 使用try…catch來捕獲除零異常

參考代碼

try {
    val i = 10 / 0

    println("你好!")
} catch {
    case ex: Exception => println(ex.getMessage)
} 

4.2、拋出異常

我們也可以在一個方法中,拋出異常。語法格式和Java類似,使用throw new Exception...

示例 | 拋出異常
示例說明

  • 在main方法中拋出一個異常

參考代碼

  def main(args: Array[String]): Unit = {
    throw new Exception("這是一個異常")
  }

Exception in thread "main" java.lang.Exception: 這是一個異常
	at ForDemo$.main(ForDemo.scala:3)
	at ForDemo.main(ForDemo.scala)
  • scala不需要在方法上聲明要拋出的異常,它已經解決了再Java中被認爲是設計失敗的檢查型異常。

5、提取器(Extractor)

我們之前已經使用過scala中非常強大的模式匹配功能了,通過模式匹配,我們可以快速匹配樣例類中的成員變量。例如:

// 1. 創建兩個樣例類
case class Person(name:String, age:Int)
case class Order(id:String)

def main(args: Array[String]): Unit = {
    // 2. 創建樣例類對象,並賦值爲Any類型
    val zhangsan:Any = Person("張三", 20)
    val order1:Any = Order("001")

    // 3. 使用match...case表達式來進行模式匹配
    // 獲取樣例類中成員變量
    order1 match {
        case Person(name, age) => println(s"姓名:${name} 年齡:${age}")
        case Order(id1) => println(s"ID爲:${id1}")
        case _ => println("未匹配")
    }
}

那是不是所有的類都可以進行這樣的模式匹配呢?答案是:

不可以的。要支持模式匹配,必須要實現一個提取器

[!NOTE]

樣例類自動實現了apply、unapply方法

5.1、定義提取器

之前我們學習過了,實現一個類的伴生對象中的apply方法,可以用類名來快速構建一個對象。伴生對象中,還有一個unapply方法。與apply相反,unapply是將該類的對象,拆解爲一個個的元素。

在這裏插入圖片描述
要實現一個類的提取器,只需要在該類的伴生對象中實現一個unapply方法即可。
語法格式

def unapply(stu:Student):Option[(類型1, 類型2, 類型3...)] = {
    if(stu != null) {
        Some((變量1, 變量2, 變量3...))
    }
    else {
        None
    }
}

示例

示例說明

  • 創建一個Student類,包含姓名年齡兩個字段
  • 實現一個類的解構器,並使用match表達式進行模式匹配,提取類中的字段。

參考代碼

class Student(var name:String, var age:Int)

object Student {
    def apply(name:String, age:Int) = {
        new Student(name, age)
    }

    def unapply(student:Student) = {
        val tuple = (student.name, student.age)

        Some(tuple)
    }
}

def main(args: Array[String]): Unit = {
    val zhangsan = Student("張三", 20)

    zhangsan match {
        case Student(name, age) => println(s"${name} => ${age}")
    }
}

6、泛型

scala和Java一樣,類和特質、方法都可以支持泛型。我們在學習集合的時候,一般都會涉及到泛型。

scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)

那如何自己定義泛型呢?

6.1、定義一個泛型方法

在scala中,使用方括號來定義類型參數。

語法格式

def 方法名[泛型名稱](..) = {
    //...
}

示例

示例說明

  • 用一個方法來獲取任意類型數組的中間的元素
    • 不考慮泛型直接實現(基於Array[Int]實現)
    • 加入泛型支持

參考代碼

不考慮泛型的實現

  def getMiddle(arr:Array[Int]) = arr(arr.length / 2)

  def main(args: Array[String]): Unit = {
    val arr1 = Array(1,2,3,4,5)

    println(getMiddle(arr1))
  }

6.2、泛型類

scala的類也可以定義泛型。接下來,我們來學習如何定義scala的泛型類
定義

語法格式

class 類[T](val 變量名: T)
  • 定義一個泛型類,直接在類名後面加上方括號,指定要使用的泛型參數
  • 指定類對應的泛型參數後,就使用這些類型參數來定義變量了

示例

示例說明

  • 實現一個Pair泛型類
  • Pair類包含兩個字段,而且兩個字段的類型不固定
  • 創建不同類型泛型類對象,並打印

參考代碼

case class Pair[T](var a:T, var b:T)

def main(args: Array[String]): Unit = {
    val pairList = List(
        Pair("Hadoop", "Storm"),
        Pair("Hadoop", 2008),
        Pair(1.0, 2.0),
        Pair("Hadoop", Some(1.9))
    )

    println(pairList)
}

6.3、泛型的上下界

需求:

我們在定義方法/類的泛型時,限定必須從哪個類繼承、或者必須是哪個類的父類。此時,就需要使用到上下界。

6.3.1、上界定義

使用<: 類型名表示給類型添加一個上界,表示泛型參數必須要從該類(或本身)繼承

語法格式

[T <: 類型]

示例
示例說明

  • 定義一個Person類
  • 定義一個Student類,繼承Person類
  • 定義一個demo泛型方法,該方法接收一個Array參數,
  • 限定demo方法的Array元素類型只能是Person或者Person的子類
  • 測試調用demo,傳入不同元素類型的Array

參考代碼

class Person
class Student extends Person

def demo[T <: Person](a:Array[T]) = println(a)

def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Student))
    // 編譯出錯,必須是Person的子類
    // demo(Array("hadoop"))
}
6.3.2、下界定義

上界是要求必須是某個類的子類,或者必須從某個類繼承,而下界是必須是某個類的父類(或本身)

語法格式

[T >: 類型]

[!NOTE]
如果類既有上界、又有下界。下界寫在前面,上界寫在後面

示例
示例說明

  • 定義一個Person類
  • 定義一個Policeman類,繼承Person類
  • 定義一個Superman類,繼承Policeman類
  • 定義一個demo泛型方法,該方法接收一個Array參數,
  • 限定demo方法的Array元素類型只能是Person、Policeman
  • 測試調用demo,傳入不同元素類型的Array

參考代碼

class Person
class Policeman extends Person
class Superman extends Policeman

def demo[T >: Policeman](array:Array[T]) = println(array)

def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 編譯出錯:Superman是Policeman的子類
    // demo(Array(new Superman))
}

6.4、協變、逆變、非變

6.4.1、非變

語法格式

class Pair[T]{}
  • 默認泛型類是非變的
  • 類型B是A的子類型,Pair[A]和Pair[B]沒有任何從屬關係
  • Java是一樣的

在這裏插入圖片描述

6.4.2、協變

語法格式

class Pair[+T]
  • 類型B是A的子類型,Pair[B]可以認爲是Pair[A]的子類型
  • 參數化類型的方向和類型的方向是一致的。
6.4.3、逆變

語法格式

class Pair[-T]
  • 類型B是A的子類型,Pair[A]反過來可以認爲是Pair[B]的子類型
  • 參數化類型的方向和類型的方向是相反的

示例
示例說明

  • 定義一個Super類、以及一個Sub類繼承自Super類
  • 使用協變、逆變、非變分別定義三個泛型類
  • 分別創建泛型類來演示協變、逆變、非變

參考代碼

class Super
class Sub extends Super

class Temp1[T]
class Temp2[+T]
class Temp3[-T]

def main(args: Array[String]): Unit = {
    val a:Temp1[Sub] = new Temp1[Sub]
    // 編譯報錯
    // 非變
    //val b:Temp1[Super] = a

    // 協變
    val c: Temp2[Sub] = new Temp2[Sub]
    val d: Temp2[Super] = c

    // 逆變
    val e: Temp3[Super] = new Temp3[Super]
    val f: Temp3[Sub] = e
}

好了,以上文章內容到這裏就結束了各位的點贊,關注就是小編堅持下去的動力。小編本着“以誠會友”的人道主義精神,來分享更多的知識哦。。
我是小哪吒,一名在校大學生。個性簽名:“我們不生產代碼,我們只做代碼的搬運工”~~~~哈哈哈

不要因爲希望去堅持,要堅持的看到希望。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章