• 11、scala类型参数


    一、类型参数1

    1、介绍

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


    2、泛型类

    // 泛型类,顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类内部,比如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


    3、泛型函数

    // 泛型函数,与泛型类类似,可以给某个函数在声明时指定泛型类型,然后在函数体内,多个变量或者返回值之间,就可以使用泛型类型进行声明,从而对某个特殊的
    变量,或者多个变量,进行强制性的类型限制。
    // 与泛型类一样,你可以通过给使用了泛型类型的变量传递值来让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


    4、上边界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> 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)
                                      ^


    5、下边界Bounds

    // 除了指定泛型类型的上边界,还可以指定下边界,即指定泛型类型必须是某个类的父类
    
    
    案例:领身份证
    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

    1、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


    2、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 cal = new Calculator(1,2)
    cal: Calculator[Int] = Calculator@60c6f5b
    
    scala> cal.max
    res0: Int = 2


    3、Manifest Context Bounds

    // 在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)


    4、协变和逆变

    // 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!


    5、Existential Type

    // 在Scala里,有一种特殊的类型参数,就是Existential Type,存在性类型。这种类型务必掌握是什么意思,因为在spark源码实在是太常见了!
    
    
    Array[T] forSome { type T }
    Array[_]
  • 相关阅读:
    Silverlight 5 系列学习之一
    WPF中数据绑定问题
    细说ASP.NET Forms身份认证 别人写的不过很透彻就转来了以后用时再看
    再学IHanlder 类----------------关于Asp.net与iis原理网上看博客收获写一个验证码用一般处理程序记的好长时间前就写过不过现在再看有点不一样的感觉
    Oracle常用查看表结构命令
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。(遇到了这个问题网上查了下保存下来)
    TxetBox PasswordChar 模式解除
    屏幕抖动一 下
    oracle 日期问题 网上找到自己查阅时方便
    day5-Dns
  • 原文地址:https://www.cnblogs.com/weiyiming007/p/11046405.html
Copyright © 2020-2023  润新知