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方法可以獲取到所有正則匹配到的字符串
示例一
示例說明
- 定義一個正則表達式,來匹配郵箱是否合法
- 合法郵箱測試:[email protected]
- 不合法郵箱測試:[email protected]
參考代碼
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)
示例三
示例說明
-
有以下郵箱列表
"[email protected]", "[email protected]", "[email protected]", "123afadff.com"
-
使用正則表達式進行模式匹配,匹配出來郵箱運營商的名字。例如:郵箱[email protected],需要將163匹配出來
- 使用括號來匹配分組
-
打印匹配到的郵箱以及運營商
參考代碼
// 使用括號表示一個分組
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
}
好了,以上文章內容到這裏就結束了各位的點贊,關注就是小編堅持下去的動力。小編本着“以誠會友”的人道主義精神,來分享更多的知識哦。。
我是小哪吒,一名在校大學生。個性簽名:“我們不生產代碼,我們只做代碼的搬運工”~~~~哈哈哈
不要因爲希望去堅持,要堅持的看到希望。 |