十一、scala類型參數

1、類型參數1es6

一、介紹數組

類型參數是什麼?類型參數其實就相似於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 ArrayList[Integer](),那麼,此時a.add(1)沒問題,
而a.add("2")呢?就不行了,由於泛型會限制,只能往集合中添加Integer類型,這樣就避免了上述的問題。

那麼Scala的類型參數是什麼?其實意思與Java的泛型是同樣的,也是定義-種類型參數,好比在集合,在類,在函數中,定義類型參數,而後就能夠保證使用到該類型
參數的地方,就確定,也只能是這種類型。從而實現程序更好的健壯性。

此外,類型參數是Spark源碼中很是常見的,所以一樣必須掌握,才能看懂spark源碼。


二、泛型類ide

// 泛型類,顧名思義,其實就是在類的聲明中,定義一些泛型類型,而後在類內部,好比field或者method,就可使用這些泛型類型。
// 使用泛型類,一般是須要對類中的某些成員,好比某些field和method中的參數或變量,進行統一的類型限制,這樣能夠保證程序更好的健壯性和穩定性。
// 若是不使用泛型進行統一的類型限制,那麼在後期程序運行過程當中,不免會出現問題,好比傳入了不但願的類型,致使程序出問題。
// 在使用類的時候,好比建立類的對象,將類型參數替換爲實際的類型,便可。
// Scala自動推斷泛型類型特性:直接給使用了泛型類型的field賦值時,Scala會自動進行類型推斷。

案例:新生報到,每一個學生來自不一樣的地方,id多是Int,多是String
scala> :paste
// Entering paste mode (ctrl-D to finish)

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

// Exiting paste mode, now interpreting.

defined class Student

scala> val leo = new Student[Int](111)        #已經定義爲Int類型
leo: Student[Int] = Student@5680a178

scala> leo.getSchool("222")            #字符串不行
<console>:13: error: type mismatch;
 found   : String("222")
 required: Int
       leo.getSchool("222")
                     ^

scala> leo.getSchool(222)
res1: String = S-222-111



scala> val jack = new Student[String]("aaa")
jack: Student[String] = Student@10bdf5e5

scala> jack.getSchool(444)
<console>:13: error: type mismatch;
 found   : Int(444)
 required: String
       jack.getSchool(444)
                      ^

scala> jack.getSchool("444")
res3: String = S-444-aaa


三、泛型函數函數

// 泛型函數,與泛型類相似,能夠給某個函數在聲明時指定泛型類型,而後在函數體內,多個變量或者返回值之間,就可使用泛型類型進行聲明,從而對某個特殊的
變量,或者多個變量,進行強制性的類型限制。
// 與泛型類同樣,你能夠經過給使用了泛型類型的變量傳遞值來讓Scala自動推斷泛型的實際類型,也能夠在調用函數時,手動指定泛型類型。

案例:卡片售賣機,能夠指定卡片的內容,內容能夠是String類型或Int類型
scala> :paste
// Entering paste mode (ctrl-D to finish)

def getCard[T](content: T) = {
  if (content.isInstanceOf[Int]) "int card: " + content
  else if (content.isInstanceOf[String]) "string card: " + content
  else "card: " + content
}

// Exiting paste mode, now interpreting.

getCard: [T](content: T)String

scala> getCard[Int](100)
res4: String = int card: 100

scala> getCard(100)
res5: String = int card: 100

scala> getCard("100")
res6: String = string card: 100


四、上邊界Boundsui

// 在指定泛型類型的時候,有時,咱們須要對泛型類型的範圍進行界定,而不是能夠是任意的類型。好比,咱們可能要求某個泛型類型,它就必須是某個類的子類,
這樣在程序中就能夠放心地調用泛型類型繼承的父類的方法,程序才能正常的使用和運行。此時就可使用上下邊界Bounds的特性。
// Scala的上下邊界特性容許泛型類型必須是某個類的子類,或者必須是某個類的父類

案例:在派對上交朋友
scala> :paste
// Entering paste mode (ctrl-D to finish)

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)
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student
defined class Party



scala> class Worker(val name: String)
defined class Worker

scala> val leo = new Student("leo")
leo: Student = Student@4b0b0854

scala> val tom = new Worker("tom")
tom: Worker = Worker@243c4f91

scala> val party = new Party(leo, tom)        #tom是Worker類型
<console>:16: error: inferred type arguments [Object] do not conform to class Party's type parameter bounds [T <: Person]
       val party = new Party(leo, tom)
                   ^
<console>:16: error: type mismatch;
 found   : Student
 required: T
       val party = new Party(leo, tom)
                             ^
<console>:16: error: type mismatch;
 found   : Worker
 required: T
       val party = new Party(leo, tom)
                                  ^


五、下邊界Boundsthis

// 除了指定泛型類型的上邊界,還能夠指定下邊界,即指定泛型類型必須是某個類的父類


案例:領身份證
scala> class Father(val name: String)
defined class Father

scala> class Child(name: String) extends Father(name)
defined class Child

scala> def getLostIDCard[T >: Child](p: T) {
     |   if (p.getClass == classOf[Child]) println("please tell us your parents' names")
     |   else if (p.getClass == classOf[Father]) println("please sign your name to get your child's lost id card.")
     |   else println("sorry, you are not allowed to get this id card.")
     | }
getLostIDCard: [T >: Child](p: T)Unit


scala> class Worker(val name: String)
defined class Worker

scala> val tom = new Worker("tom")
tom: Worker = Worker@45ca843

scala> getLostIDCard(tom)
sorry, you are not allowed to get this id card.

scala> val jack = new Father("jack")
jack: Father = Father@78123e82

scala> val leo = new Child("leo")
leo: Child = Child@58d75e99

scala> getLostIDCard(jack)
please sign your name to get your child's lost id card.

scala> getLostIDCard(leo)
please tell us your parents' names


2、類型參數2es5

一、View Boundsspa

// 上下邊界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


二、Context Boundsscala

// Context Bounds是一種特殊的Bounds,它會根據泛型類型的聲明,好比「T: 類型」要求必須存在一個類型爲「類型[T]」的隱式值。其實我的認爲,Context Bounds之因此叫Context,是由於它基於的是一種全局的上下文,須要使用到上下文中的隱式值以及注入。

案例:使用Scala內置的比較器比較大小
scala> :paste
// Entering paste mode (ctrl-D to finish)

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
}

// Exiting paste mode, now interpreting.

defined class Calculator

scala> val cal = new Calculator(1,2)
cal: Calculator[Int] = Calculator@60c6f5b

scala> cal.max
res0: Int = 2


三、Manifest Context Boundscode

// 在Scala中,若是要實例化一個泛型數組,就必須使用Manifest Context Bounds。也就是說,若是數組元素類型爲T的話,須要爲類或者函數定義[T: Manifest]泛型類型,這樣才能實例化Array[T]這種泛型數組。

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

scala> class Vegetable(val name: String)
defined class Vegetable

scala> :paste
// Entering paste mode (ctrl-D to finish)

def packageFood[T: Manifest] (foods: T*) = {
  val foodPackage = new Array[T](foods.length)
  for (i <- 0 until foods.length) foodPackage(i) = foods(i)
  foodPackage
}

// Exiting paste mode, now interpreting.

packageFood: [T](foods: T*)(implicit evidence$1: Manifest[T])Array[T]

scala> val gongbaojiding = new Meat("gongbaojiding")
gongbaojiding: Meat = Meat@295cf707

scala> val yuxiangrousi = new Meat("yuxiangrousi")
yuxiangrousi: Meat = Meat@6b58b9e9

scala> val shousiyangpai = new Meat("shousiyangpai")
shousiyangpai: Meat = Meat@125290e5

scala> val meatPackage = packageFood(gongbaojiding, yuxiangrousi, shousiyangpai)
meatPackage: Array[Meat] = Array(Meat@295cf707, Meat@6b58b9e9, Meat@125290e5)


scala> val qingcai = new Vegetable("qingcai")
qingcai: Vegetable = Vegetable@319988b0

scala> val baicai = new Vegetable("baicai")
baicai: Vegetable = Vegetable@78aea4b9

scala> val huanggua = new Vegetable("huanggua")
huanggua: Vegetable = Vegetable@47428937

scala> val vegPackage = packageFood(qingcai, baicai, huanggua)
vegPackage: Array[Vegetable] = Array(Vegetable@319988b0, Vegetable@78aea4b9, Vegetable@47428937)


四、協變和逆變

// Scala的協變和逆變是很是有特點的!徹底解決了Java中的泛型的一大缺憾!
// 舉例來講,Java中,若是有Professional是Master的子類,那麼Card[Professionnal]是否是Card[Master]的子類?答案是:不是。所以對於開發程序形成了不少的麻煩。
// 而Scala中,只要靈活使用協變和逆變,就能夠解決Java泛型的問題。

案例:進入會場
scala> class Master
defined class Master

scala> class Professional extends Master
defined class Professional


//大師以及大師級別如下的名片均可以進入會場
scala> class Card[+T] (val name: String)
defined class Card

scala> val leo = new Card[Master]("leo")
leo: Card[Master] = Card@7c28c1

scala> val jack = new Card[Professional]("jack")
jack: Card[Professional] = Card@54da32dc

scala> def enterMeet(card: Card[Master]) {
     |   println("welcome to have this meeting")
     | }
enterMeet: (card: Card[Master])Unit

scala> enterMeet(leo)
welcome to have this meeting

scala> enterMeet(jack)
welcome to have this meeting



//只要專家級別的名片就能夠進入會場,若是大師級別的過來了,固然能夠了!
scala> class Card[-T](val name: String)
defined class Card

scala> val leo = new Card[Master]("leo")
leo: Card[Master] = Card@15cea7b0

scala> val jack = new Card[Professional]("jack")
jack: Card[Professional] = Card@2a22ad2b

scala> def enterMeet(card: Card[Professional]) {
     |   println("welcome to have this meeting!")
     | }
enterMeet: (card: Card[Professional])Unit

scala> enterMeet(jack)
welcome to have this meeting!

scala> enterMeet(leo)
welcome to have this meeting!


五、Existential Type

// 在Scala裏,有一種特殊的類型參數,就是Existential Type,存在性類型。這種類型務必掌握是什麼意思,由於在spark源碼實在是太常見了!


Array[T] forSome { type T }
Array[_]
相關文章
相關標籤/搜索