Scala程序設計基礎(四)

異常處理

  • 類似Java,使用try..catch捕獲處理異常
  • 不同的是catch中使用模式匹配的思想
	try {
	    // 代碼
	}
	catch {
	    case ex:異常類型1 => // 代碼
	    case ex:異常類型2 => // 代碼
	}
	finally {	// 不管是否出現異常都會執行
	    // 代碼
	}
  • 同樣,可以使用throw拋出異常

提取器

  • 是一個帶有unapply方法的對象
  • 從傳遞給它的對象中提取出構造該對象的參數
    在這裏插入圖片描述
  • 類似於apply()方法,需要在伴生對象中使用
class Student {
  var name:String = _   // 姓名
  var age:Int = _       // 年齡
  
  // 實現一個輔助構造器
  def this(name:String, age:Int) = {
    this()	// 父類構造方法
    this.name = name
    this.age = age
  }
}

object Student {	// 伴生對象
  // 構造器
  def apply(name:String, age:Int): Student = new Student(name, age)

  // 解構器
  def unapply(arg: Student): Option[(String, Int)] = Some(arg.name, arg.age))
}

object extractor_DEMO {
  def main(args: Array[String]): Unit = {
    val zhangsan = Student("張三", 20)	// 使用伴生對象的apply方法直接實例化

    zhangsan match {	// 自動實現實例類的解構方法
      case Student(name, age) => println(s"姓名:$name 年齡:$age")
      case _ => println("未匹配")
    }
  }
}

泛型

  • 泛型用於指定方法或類可以接受任意類型參數,參數在實際使用時才被確定
  • 使用泛型可以使得類或方法具有更強的通用性,泛型無處不在
  • 使用方括號[ ]來定義類型參數(注意區分類型與類型參數)
  def getMiddle[A](arr:Array[A]) = arr(arr.length / 2)

  def main(args: Array[String]): Unit = {
    val arr1 = Array(1,2,3,4,5)
    val arr2 = Array("a", "b", "c", "d", "f")

    println(getMiddle[Int](arr1))	// 傳參時確定類型
    println(getMiddle[String](arr2))

    // 簡寫方式
    println(getMiddle(arr1))
    println(getMiddle(arr2))
  }
  • 泛型類
// 類名後面的方括號,就表示這個類可以使用兩個類型、分別是T和S
// 這個名字可以任意取,可以叫W或者C
class Pair[T, S](val first: T, val second: S)

case class People(var name:String, val age:Int)

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

  val p1 = new Pair[String, Int]("張三", 10)
  val p2 = new Pair[String, String]("張三", "1988-02-19")
  val p3 = new Pair[People, People](People("張三", 20), People("李四", 30))
  }
}
  • 隨着泛型還有一些其他概念:

上下界

  • 在指定泛型類型時,有時需要界定泛型類型的範圍,而不是接收任意類型
  • 上下邊界特性允許泛型類型是某個類的子類,或者是某個類的父類
//要控制Person只能和Person、Policeman聊天,但是不能和Superman聊天。此時,還需要給泛型添加一個下界。
//上下界
class Pair[T <: Person, S >: Policeman <:Person](val first: T, val second: S) {
  def chat(msg:String) = println(s"${first.name}對${second.name}說: $msg")
}

class Person(var name:String, val age:Int)
class Policeman(name:String, age:Int) extends Person(name, age)
class Superman(name:String) extends Policeman(name, -1)

object Pair {
  def main(args: Array[String]): Unit = {
//運行錯誤:第二個參數必須是Person的子類(包括本身)、Policeman的父類(包括本身)
    val p3 = new Pair[Person,Superman](new Person("張三", 20), new Superman("李四"))
    p3.chat("你好!")
  }
}

協變

  • class Pair[+T],這種情況是協變。類型B是A的子類型,那麼Pair[B]可以認爲是Pair[A]的子類型
  • 這種情況,類型參數的方向和類型的方向是一致的
class Pair[+T](a:T)

object Pair {
  def main(args: Array[String]): Unit = {
    val p1 = new Pair("hello")
    // String是AnyRef的子類
    val p2:Pair[AnyRef] = p1
    println(p2)
  }
}

逆變

  • class Pair[-T],這種情況是逆變。類型B是A的子類型,Pair[A]反過來可以認爲是Pair[B]的子類型
  • 這種情況,類型參數的方向和類型的方向是相反的

非變

  • class Pair[T],這種情況就是非變(默認),類型B是A的子類型,Pair[A]和Pair[B]沒有任何從屬關係
class Super
class Sub extends Super

//非變
class Temp1[A](title: String)
//協變
class Temp2[+A](title: String)
//逆變
class Temp3[-A](title: String)

object Covariance_demo {
  def main(args: Array[String]): Unit = {
    val a = new Sub()
    // 與Java同樣,向上轉型,不屬於泛型
    val b:Super = a

    // 非變
    val t1:Temp1[Sub] = new Temp1[Sub]("測試")
    // 報錯!默認不允許轉換
    // val t2:Temp1[Super] = t1

    // 協變
    val t3:Temp2[Sub] = new Temp2[Sub]("測試")
    val t4:Temp2[Super] = t3	// 類似Java普通類向上轉型
    
    // 逆變
    val t5:Temp3[Super] = new Temp3[Super]("測試")
    val t6:Temp3[Sub] = t5		// 類似Java普通類向下轉型
  }
}

-更加詳細的內容可以參考說明或者官方文檔

隱式轉換和隱式參數

  • 可以允許你手動指定,將某種類型的對象轉換成其他類型的對象或者是給一個類增加方法
  • 隱式轉換:使用 implicit 修飾的方法實現把一個原始類轉換成目標類,進而可以調用目標類中的方法
  • 所有的隱式轉換和隱式參數必須定義在一個object中
//todo:一個類隱式轉換成具有相同方法的多個類

class C
class A(c:C) {
  def readBook(): Unit ={
    println("A說:好書好書...")
  }
}

class B(c:C){
  def readBook(): Unit ={
    println("B說:看不懂...")
  }
  def writeBook(): Unit ={
    println("B說:不會寫...")
  }
}

object AB{
  // 將原始類C轉換爲目標類A B
  // 此時的C就有了A/B的能力
  implicit def C2A(c:C) = new A(c)
  implicit def C2B(c:C) = new B(c)
}

object B{
  def main(args: Array[String]) {
    //導包
    //1. import AB._ 會將AB類下的所有隱式轉換導進來
    //2. import AB.C2A 只導入C類到A類的的隱式轉換方法
    //3. import AB.C2B 只導入C類到B類的的隱式轉換方法
    import AB._
    val c=new C

    //由於A類與B類中都有readBook(),只能導入其中一個,否則會衝突
    //c.readBook()

    //C類可以執行B類中的writeBook()  即C2B
    c.writeBook()
  }
}
  • 隱式參數:在函數或者方法中,定義一個用implicit修飾的參數,Scala會嘗試找到一個指定類型的用implicit修飾的參數,即隱式值,傳入方法
  • Scala會在兩個範圍內查找:一種是當前作用域內可見的val或var定義的隱式變量(導包);一種是隱式參數類型的伴生對象內的隱式值
//todo:隱式參數案例:員工領取薪水

object Company{
  // 在object中定義用implicit修飾的參數(隱式參數)
  // 注意:同一類型的隱式值只允許出現一次,否則會報錯
  implicit val xxx="zhangsan"
  implicit val yyy=10000.00
  //implicit  val zzz="lisi"
}

class Boss {
  // 定義一個用implicit修飾的參數,類型爲String
  // 注意參數匹配的類型   它需要的是String類型的隱式值
  def callName(implicit name:String):String={
    name+" is coming !"
  }

  // 定義一個用implicit修飾的參數,類型爲Double
  // 注意參數匹配的類型    它需要的是Double類型的隱式值
  def getMoney(implicit money:Double):String={
    " 當月薪水:"+money
  }
}

object Boss extends App{
  //使用import導入定義好的隱式值,注意:必須先加載否則會報錯
  import Company.xxx	// String類型的隱式值
  import Company.yyy	// Int類型的隱式值

  val boss = new Boss
  // Scala會嘗試找到一個指定類型的用implicit修飾的參數,即隱式值,傳入方法
  println(boss.callName+boss.getMoney)

}

結語

以上四篇基本介紹了在大數據開發過程中經常使用到的基本Scala語法,這並不是全面的語法總結。Scala是一門多範式編程語言,有很強的靈活性,掌握思想比較關鍵,後面會通過案例和源碼的方式呈現基本原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章