十一、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[_]
相关文章
相关标签/搜索