Spark基礎-scala學習(七、型別引數)
阿新 • • 發佈:2018-12-16
型別引數是什麼
- 類似於java泛型,泛型類
- 泛型函式
- 上邊界Bounds
- 下邊界
- View Bounds
- Context Bounds
- Manifest Context Bounds
- 協變和逆變
- Existential Type
泛型類
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Student[T](val localId:T){
def getSchoolId(hukouId:T) = "S-"+hukouId+"-"+localId
}
// Exiting paste mode, now interpreting.
defined class Student
scala> val s = new Student[Int](11)
s: Student[Int] = [email protected]
scala> s.getSchoolId(234)
res1: String = S-234-11
泛型函式
- 泛型函式,與泛型類類似,可以給某個函式在宣告時指定泛型型別,然後在函式體內,多個變數或者返回值之間,就可以使用泛型型別進行宣告,從而對某個特殊的變數,或者多個變數,進行強制性的型別限制
- 與泛型類一樣,你可以通過使用了泛型型別的變數傳遞值來讓Scala自動推斷泛型的實際型別,也可以在呼叫函式時,手動指定泛型型別
scala> :paste // Entering paste mode (ctrl-D to finish) 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 } // Exiting paste mode, now interpreting. getCard: [T](content: T)String scala> getCard[String]("leo") res2: String = card:this is your card, leo scala> getCard[Int](123) res3: String = card: 001,123
上邊界Bounds
- 在指定泛型型別的時候,有時,我們需要對泛型型別的範圍進行界定,而不是可以是任意的型別。比如,我們可能要求某個泛型型別,他就必須是某個類的子類,這樣在程式中就可以放心地呼叫泛型型別繼承的父類的方法,程式才能正常的使用和執行。此時就可以使用上下邊界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> val p = new Person("Tom")
p: Person = [email protected]
scala> val p2 = new Person("leo")
p2: Person = [email protected]
scala> p.makeFriends(p2)
Hello,I'm Tom
Hello,I'm leo
scala> val s1 = new Student("Jarry")
s1: Student = [email protected]
scala> val s2 = new Student("Marry")
s2: Student = [email protected]
scala> val pa = new Party[Student](s1,s2)
pa: Party[Student] = [email protected]
scala> pa.play
Hello,I'm Jarry
Hello,I'm Marry
下邊界Bounds
- 除了指定泛型型別的上邊界,還可以指定下邊界,即指定泛型型別必須是某個類的父類
scala> :paste
// Entering paste mode (ctrl-D to finish)
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.")
}
// Exiting paste mode, now interpreting.
defined class Father
defined class Child
getIDCard: [R >: Child](person: R)Unit
scala> val f = new Father("fa")
f: Father = [email protected]
scala> val c = new Child("cd")
c: Child = [email protected]
scala> getIDCard[Father](f)
sign your name for your child's id card.
scala> getIDCard[Child](c)
please tell us your parents' names.
View Bounds
- 上下邊界Bounds,雖然可以讓一種泛型型別,支援有父子關係的多種型別。但是,在某個類與上下邊界Bounds指定的父子類型範圍內的類都沒有任何關係,則預設是肯定不能接受的
- 然而,View Bounds作為一種上下邊界Bounds的加強版,支援可以對型別進行隱式轉換,將指定的型別進行隱式轉換後,再判斷是否在邊界指定的類型範圍內
- 案例:跟小狗交朋友
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 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)
// Exiting paste mode, now interpreting.
<pastie>:23: warning: implicit conversion method dog2person should be enabled
by making the implicit value scala.language.implicitConversions visible.
This can be achieved by adding the import clause 'import scala.language.implicitConversions'
or by setting the compiler option -language:implicitConversions.
See the Scaladoc for value scala.language.implicitConversions for a discussion
why the feature should be explicitly enabled.
implicit def dog2person(dog: Object):Person = if(dog.isInstanceOf[Dog]){val _dog = dog.asInstanceOf[Dog];new Person(_dog.name)} else Nil
^
defined class Person
defined class Student
defined class Dog
dog2person: (dog: Object)Person
defined class Party
scala> val leo = new Student("leo")
leo: Student = [email protected]
scala> val doggy = new Dog("doggy")
doggy: Dog = [email protected]
scala> val party = new Party(leo,doggy)
party: Party[Object] = [email protected]
Context Bounds
- 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 ca = new Calculator[Int](12,23)
ca: Calculator[Int] = [email protected]
scala> ca.max
res8: Int = 23
Manifest Context Bounds
- 在scala中,如果要例項化一個泛型陣列,就必須使用Manifest Context Bounds,也就是說,如果陣列元素型別為T的話,需要為類或者函式定義[T:Manifest]泛型型別,這樣才能例項化Array[T]這種泛型陣列
- 案例:打包飯菜(一種食品打成一包)
scala> :paste
// Entering paste mode (ctrl-D to finish)
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
}
// Exiting paste mode, now interpreting.
defined class Meat
defined class Vegetable
packageFood: [T](food: T*)(implicit evidence$1: Manifest[T])Array[T]
scala> val gongbaojiding = new Meat("gongbaojiding")
gongbaojiding: Meat = [email protected]
scala> val shoushibaocai = new Meat("shoushibaocai")
shoushibaocai: Meat = [email protected]
scala> val meatPackage = packageFood(gongbaojiding,shoushibaocai)
meatPackage: Array[Meat] = Array([email protected], [email protected])
協變和逆變
- scala的協變和逆變完全解決了java中的泛型的一大缺憾
- 舉例來說,java中,如果有professional是Master的子類,那麼Card(Professionnal)是不是Card(Master)的子類呢?答案是:不是。
- 而scala中,只要靈活使用協變和逆變,就可以解決java泛型的問題
- 案例:進入會場
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Master
class Professional extends Master
//大師以及大師級別以下的名片都可以進入會場
class Card[+T](val name:String)
def enterMeet(card:Card[Master]){
println("Welcome to have this meeting")
}
// Exiting paste mode, now interpreting.
defined class Master
defined class Professional
defined class Card
enterMeet: (card: Card[Master])Unit
scala> val leo = new Card[Master]("leo")
leo: Card[Master] = [email protected]
scala> val jack = new Card[Professional]("jack")
jack: Card[Professional] = [email protected]
scala> enterMeet(leo)
Welcome to have this meeting
scala> enterMeet(jack)
Welcome to have this meeting
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Card[-T](val name:String)
def enterMeet(card:Card[Professional]){
println("welcome to have this meeting!")
}
// Exiting paste mode, now interpreting.
defined class Card
enterMeet: (card: Card[Professional])Unit
scala> val leo = new Card[Master]("leo")
leo: Card[Master] = [email protected]
scala> val jack = new Card[Professional]("jack")
jack: Card[Professional] = [email protected]
scala> enterMeet(leo)
welcome to have this meeting!
scala> enterMeet(jack)
welcome to have this meeting!
Existential Type
- 在scala中,有一種特殊的型別引數,就是Existential Type存在性型別。
Array[T] forSome {type T}
//佔位符
Array[_]