1. 程式人生 > >scala筆記-型別引數(15)

scala筆記-型別引數(15)

型別引數是什麼?

型別引數其實就類似於Java中的泛型。先說說Java中的泛型是什麼,比如我們有List a = new ArrayList(),接著a.add(1),沒問題,a.add(“2”),然後我們a.get(1) == 2,對不對?肯定不對了,a.get(1)獲取的其實是個String——“2”,String——"2"怎麼可能與一個Integer型別的2相等呢? 所以Java中提出了泛型的概念,其實也就是型別引數的概念,此時可以用泛型建立List,List a = new ArrayListInteger,那麼,此時a.add(1)沒問題,而a.add(“2”)呢?就不行了,因為泛型會限制,只能往集合中新增Integer型別,這樣就避免了上述的問題。 那麼Scala的型別引數是什麼?其實意思與Java的泛型是一樣的,也是定義一種型別引數,比如在集合,在類,在函式中,定義型別引數,然後就可以保證使用到該型別引數的地方,就肯定,也只能是這種型別。從而實現程式更好的健壯性。

泛型類
// 泛型類,顧名思義,其實就是在類的宣告中,定義一些泛型型別,然後在類內部,比如field或者method,就可以使用這些泛型型別。
// 使用泛型類,通常是需要對類中的某些成員,比如某些field和method中的引數或變數,進行統一的型別限制,這樣可以保證程式更好的健壯性和穩定性。
// 如果不使用泛型進行統一的型別限制,那麼在後期程式執行過程中,難免會出現問題,比如傳入了不希望的型別,導致程式出問題。
// 在使用類的時候,比如建立類的物件,將型別引數替換為實際的型別,即可。
// Scala自動推斷泛型型別特性:直接給使用了泛型型別的field賦值時,Scala會自動進行型別推斷。

案例:新生報到,每個學生來自不同的地方,id可能是Int,可能是String

class Student[T](val localId: T) {
  def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId
}

val leo = new Student[Int](111)
泛型函式
// 泛型函式,與泛型類類似,可以給某個函式在宣告時指定泛型型別,然後在函式體內,多個變數或者返回值之間,就可以使用泛型型別進行宣告,從而對某個特殊的變數,或者多個變數,進行強制性的型別限制。
// 與泛型類一樣,你可以通過給使用了泛型型別的變數傳遞值來讓Scala自動推斷泛型的實際型別,也可以在呼叫函式時,手動指定泛型型別。

案例:卡片售賣機,可以指定卡片的內容,內容可以是String型別或Int型別
def getCard[T](content: T) = {
  if(content.isInstanceOf[Int]) "card: 001, " + content
  else if(content.isInstanceOf[String]) "card: this is your card, " + content
  else "card: " + content
}

getCard[String]("hello world")
上邊界Bounds
// 在指定泛型型別的時候,有時,我們需要對泛型型別的範圍進行界定,而不是可以是任意的型別。比如,我們可能要求某個泛型型別,它就必須是某個類的子類,這樣在程式中就可以放心地呼叫泛型型別繼承的父類的方法,程式才能正常的使用和執行。此時就可以使用上下邊界Bounds的特性。
// Scala的上下邊界特性允許泛型型別必須是某個類的子類,或者必須是某個類的父類

案例:在派對上交朋友
class Person(val name: String) {
  def sayHello = println("Hello, I'm " + name)
  def makeFriends(p: Person) {
    sayHello
    p.sayHello
  }
}
class Student(name: String) extends Person(name)
class Party[T <: Person](p1: T, p2: T) {
  def play = p1.makeFriends(p2)
}
下邊界Bounds
// 除了指定泛型型別的上邊界,還可以指定下邊界,即指定泛型型別必須是某個類的父類

案例:領身份證
class Father(val name: String) 
class Child(name: String) extends Father(name)

def getIDCard[R >: Child](person: R) {
  if (person.getClass == classOf[Child]) println("please tell us your parents' names.")
  else if (person.getClass == classOf[Father]) println("sign your name for your child's id card.")
  else println("sorry, you are not allowed to get id card.")
}
View Bounds
// 上下邊界Bounds,雖然可以讓一種泛型型別,支援有父子關係的多種型別。但是,在某個類與上下邊界Bounds指定的父子類型範圍內的類都沒有任何關係,則預設是肯定不能接受的。
// 然而,View Bounds作為一種上下邊界Bounds的加強版,支援可以對型別進行隱式轉換,將指定的型別進行隱式轉換後,再判斷是否在邊界指定的類型範圍內

案例:跟小狗交朋友
class Person(val name: String) {
  def sayHello = println("Hello, I'm " + name)
  def makeFriends(p: Person) {
    sayHello
    p.sayHello
  }
}
class Student(name: String) extends Person(name)
class Dog(val name: String) { def sayHello = println("Wang, Wang, I'm " + name) }

implicit def dog2person(dog: Object): Person = if(dog.isInstanceOf[Dog]) {val _dog = dog.asInstanceOf[Dog]; new Person(_dog.name) } else Nil

class Party[T <% Person](p1: T, p2: T)
Context Bounds
// Context Bounds是一種特殊的Bounds,它會根據泛型型別的宣告,比如“T: 型別”要求必須存在一個型別為“型別[T]”的隱式值。其實個人認為,Context Bounds之所以叫Context,是因為它基於的是一種全域性的上下文,需要使用到上下文中的隱式值以及注入。

案例:使用Scala內建的比較器比較大小
class Calculator[T: Ordering] (val number1: T, val number2: T) {
  def max(implicit order: Ordering[T]) = if(order.compare(number1, number2) > 0) number1 else number2
}
Manifest Context Bounds
// 在Scala中,如果要例項化一個泛型陣列,就必須使用Manifest Context Bounds。也就是說,如果陣列元素型別為T的話,需要為類或者函式定義[T: Manifest]泛型型別,這樣才能例項化Array[T]這種泛型陣列。

案例:打包飯菜(一種食品打成一包)
class Meat(val name: String)
class Vegetable(val name: String)

def packageFood[T: Manifest] (food: T*) = {
  val foodPackage = new Array[T](food.length)
  for(i <- 0 until food.length) foodPackage(i) = food(i)
  foodPackage 
}
協變和逆變
// Scala的協變和逆變是非常有特色的!完全解決了Java中的泛型的一大缺憾!
// 舉例來說,Java中,如果有Professional是Master的子類,那麼Card[Professionnal]是不是Card[Master]的子類?答案是:不是。因此對於開發程式造成了很多的麻煩。
// 而Scala中,只要靈活使用協變和逆變,就可以解決Java泛型的問題。

案例:進入會場
class Master
class Professional extends Master

// 大師以及大師級別以下的名片都可以進入會場
class Card[+T] (val name: String)
def enterMeet(card: Card[Master]) {
  println("welcome to have this meeting!")
}

// 只要專家級別的名片就可以進入會場,如果大師級別的過來了,當然可以了!
class Card[-T] (val name: String)
def enterMeet(card: Card[Professional]) {
  println("welcome to have this meeting!")
}
Existential Type
 // 在Scala裡,有一種特殊的型別引數,就是Existential Type,存在性型別。這種型別務必掌握是什麼意思,因為在spark原始碼實在是太常見了!
 
 Array[T] forSome { type T }
 Array[_]