13、Scala基础之类型参数

引言

类型参数是什么?类型参数其实就类似于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源码。

泛型类

泛型类,顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类内部,比如field或者method,就可以使用这些泛型类型。
使用泛型类,通常是需要对类中的某些成员,比如某些field和method中的参数或变量,进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。
如果不使用泛型进行统一的类型限制,那么在后期程序运行过程中,难免会出现问题,比如传入了不希望的类型,导致程序出问题。
在使用类的时候,比如创建类的对象,将类型参数替换为实际的类型,即可。
Scala自动推断泛型类型特性:直接给使用了泛型类型的field赋值时,Scala会自动进行类型推断。
案例:新生报到,每个学生来自不同的地方,id可能是Int,可能是String

class Student[T](val localId:T) {
  def getSchool(hukouId:T) = "Student-" + hukouId + "-" + localId
}
object Main {
  def main(args: Array[String]): Unit = {
    var s = new Student[Int](1111)
    println(s.getSchool(222))
    var s1 = new Student[String]("1111")
    println(s1.getSchool("2222"))
    // 自动推断
    var s2 = new Student("1111")
    println(s2.getSchool("2222"))
  }
}

泛型函数

泛型函数,与泛型类类似,可以给某个函数在声明时指定泛型类型,然后在函数体内,多个变量或者返回值之间,就可以使用泛型类型进行声明,从而对某个特殊的变量,或者多个变量,进行强制性的类型限制。
与泛型类一样,你可以通过给使用了泛型类型的变量传递值来让Scala自动推断泛型的实际类型,也可以在调用函数时,手动指定泛型类型。
案例:卡片售卖机,可以指定卡片的内容,内容可以是String类型或Int类型

object Main {
  def main(args: Array[String]): Unit = {
    getCard[Int](100)
    getCard[String]("100")
    getCard(110)
  }
  def getCard[T](content:T): Unit = {
    if(content.getClass == classOf[Integer]) println("int card:" + content)
    else if(content.getClass == classOf[String]) println("String card:" + content)
    else println("card:" + content)
  }
}

上边界Bounds

在指定泛型类型的时候,有时,我们需要对泛型类型的范围进行界定,而不是可以是任意的类型。比如,我们可能要求某个泛型类型,它就必须是某个类的子类,这样在程序中就可以放心地调用泛型类型继承的父类的方法,程序才能正常的使用和运行。此时就可以使用上下边界Bounds的特性。
Scala的上下边界特性允许泛型类型必须是某个类的子类,或者必须是某个类的父类
案例:在派对上交朋友

class Person(val name:String) {
  def sayHello = println("Hello, my name is " + name)
  def makefriends(p:Person): Unit = {
    sayHello
    p.sayHello
  }
}
class Student(name:String) extends Person(name) {
}
class Party[T <: Person](p1:T, p2:T) {
  def play = p1.makefriends(p2)
}
object Main {
  def main(args: Array[String]): Unit = {
    var s1 = new Student("Zhao Jun")
    var s2 = new Student("Feng Xiangbin")
    var p = new Party[Student](s1, s2)
    p.play
  }
}

下边界Bounds

除了指定泛型类型的上边界,还可以指定下边界,即指定泛型类型必须是某个类的父类
案例:领身份证

class Person(val name:String) {
  
}
class Parent(val name:String) {

}
class Child(name:String) extends Parent(name) {

}
object Main {
  def main(args: Array[String]): Unit = {
    var father = new Parent("parent")
    var child = new Child("child")
    var person =new Person("person")
    getIdGard(father)
    getIdGard(child)
    getIdGard(person)

  }
  def getIdGard[T >: Child](p:T): Unit = {
    if(p.getClass == classOf[Child]) println("please tello me your parent name")
    else if(p.getClass == classOf[Parent]) println("please sign your parent name to get your id card")
    else println("your cann't get id card")
  }
}

View Bounds

上下边界Bounds,虽然可以让一种泛型类型,支持有父子关系的多种类型。但是,在某个类与上下边界Bounds指定的父子类型范围内的类都没有任何关系,则默认是肯定不能接受的。
然而,View Bounds作为一种上下边界Bounds的加强版,支持可以对类型进行隐式转换,将指定的类型进行隐式转换后,再判断是否在边界指定的类型范围内
案例:跟小狗交朋友

class Person(val name:String) {
  def sayHello = println("Hello, my name is " + name)
  def makefriends(p:Person): Unit = {
    sayHello
    p.sayHello
  }
}
class Student(name:String) extends Person(name) {
}
class Dog(val name:String) {
  def sayHello = println("wang wang, my name is " + name)
}
class Party[T <% Person](p1:T, p2:T) {
  def play = p1.makefriends(p2)
}
object Main {
  def main(args: Array[String]): Unit = {
    val s = new Student("Z J")
    val d = new Dog("D J")
    val person = dog2person(d)
    val party = new Party(s, person)
    party.play
  }

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

Context Bounds

Context Bounds是一种特殊的Bounds,它会根据泛型类型的声明,比如“T: 类型”要求必须存在一个类型为“类型[T]”的隐式值。其实个人认为,Context Bounds之所以叫Context,是因为它基于的是一种全局的上下文,需要使用到上下文中的隐式值以及注入。
案例:使用Scala内置的比较器比较大小

class Calculator[T:Ordering](val num1:T, val num2:T) {
  def getMax(implicit order: Ordering[T]) = if(order.compare(num1, num2) > 0) num1 else num2
}
object Main {
  def main(args: Array[String]): Unit = {
    var c = new Calculator(1,2)
    println(c.getMax)
  }
}

Manifest Context Bounds

在Scala中,如果要实例化一个泛型数组,就必须使用Manifest Context Bounds。也就是说,如果数组元素类型为T的话,需要为类或者函数定义[T: Manifest]泛型类型,这样才能实例化Array[T]这种泛型数组。
案例:打包饭菜(一种食品打成一包)

class Meat(val name:String) {

}
class Vegetable(val name:String) {

}
object Main {
  def main(args: Array[String]): Unit = {
    val meat1 = new Meat("yuxiangrousi")
    val meat2 = new Meat("xiaosurou")
    val meat3 = new Meat("hongshaoshizitou")
    val vegetable1 = new Vegetable("xiaoqingcai")
    val vegetable2 = new Vegetable("baicai")
    val vegetable3 = new Vegetable("shanghaiqing")
    val objects1 = packageFood(meat1, meat2, meat3)
    println(objects1(0).getClass)
    val objects2 = packageFood(vegetable1, vegetable1, vegetable1)
    println(objects2(0).getClass)
  }
  def packageFood[T:Manifest](food:T*) ={
    val packageFood = new Array[T](food.length)
    for(i <- 0 until food.length) {
      packageFood(i) = food(i)
    }
    packageFood
  }
}

协变和逆变

Scala的协变和逆变是非常有特色的!完全解决了Java中的泛型的一大缺憾!
举例来说,Java中,如果有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?答案是:不是。因此对于开发程序造成了很多的麻烦。
而Scala中,只要灵活使用协变和逆变,就可以解决Java泛型的问题。
案例:进入会场
大师以及大师级别以下的名片都可以进入会场

class Master {

}
class Professional extends Master {

}
class Card[+T](val name:String) {

}
object Main {
  def main(args: Array[String]): Unit = {
    val m = new Master
    val p = new Professional
    var cm = new Card[Master]("Zhao Jun")
    var cp = new Card[Professional]("Feng Xiangbin")
    enterMetting(cm)
    enterMetting(cp)
  }

  def enterMetting(card: Card[Master]): Unit ={
    println("hello " + card.name +", welcome to this metting")
  }
}

只要专家级别的名片就可以进入会场,如果大师级别的过来了,当然可以了

class Master {

}
class Professional extends Master {

}
class Card[-T](val name:String) {

}
object Main {
  def main(args: Array[String]): Unit = {
    val m = new Master
    val p = new Professional
    var cm = new Card[Master]("Zhao Jun")
    var cp = new Card[Professional]("Feng Xiangbin")
    enterMetting(cm)
    enterMetting(cp)
  }

  def enterMetting(card: Card[Professional]): Unit ={
    println("hello " + card.name +", welcome to this metting")
  }
}

Existential Type

在Scala里,有一种特殊的类型参数,就是Existential Type,存在性类型。这种类型务必掌握是什么意思,因为在spark源码实在是太常见了!

Array[T] forSome { type T }
Array[_]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343