zoukankan      html  css  js  c++  java
  • Scala入门系列(十三):类型参数

    引言

    Scala中类型参数是什么呢?其实就类似于Java中的泛型。定义一种类型参数,比如在集合、类、函数中定义类型参数,然后就可以保证使用到该类型参数的地方就只能是这种类型,从而实现程序更好的健壮性。

    泛型类

    泛型类,顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类内部,比如field或method,就可以使用这些泛型类型。

    使用泛型类,通常是需要对类中的某些成员,比如某些field和method中的参数或变量,进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。

    在使用类的时候,比如创建类的对象,将类型参数替换为实际的类型即可,甚至可以直接赋值,Scala会自动进行类型推断

    // 定义泛型类
    // 语法:类名[泛型标识], 泛型标识可以任意设定,常用(T, V, U, K, W,甚至是_)
    // 案例:新生报到,需要为每个学生生成ID
    class Student[T] (val localId: T) {
    def getSchoolId(homeId: T) = "S-" + homeId + "-" + localId
    }
    defined class Student
    // 测试,指定该泛型为整数类型
    scala> val leo = new Student[Int](111)
    leo: Student[Int] = Student@129b69b2
    // 这样当我们传入字符类型时就会报错
    scala> leo.getSchoolId("222")
    <console>:13: error: type mismatch;
    found : String("222")
    required: Int
    leo.getSchoolId("222")
    ^
    // 传入整数类型OK
    scala> leo.getSchoolId(222)
    res26: String = S-222-111
    // 测试:不指定类型,直接传入类型,Scala会自动进行类型推断
    scala> val leo = new Student("111")
    leo: Student[String] = Student@4990b335
    scala> leo.getSchoolId(222)
    <console>:13: error: type mismatch;
    found : Int(222)
    required: String
    leo.getSchoolId(222)
    ^
    scala> leo.getSchoolId("222")
    res30: String = S-222-111

    泛型函数

    因为函数在Scala中跟类一样也是一等公民,所以泛型也同样可以作用于函数。

    // 定义泛型函数, 案例:卡片售卖机
    def getCard[T](content: T) = {
    if(content.isInstanceOf[Int]) "int card: " + content
    else if(content.isInstanceOf[String]) "String card: " + content
    else "card: " + content
    }
    getCard: [T](content: T)String
    // 测试
    scala> getCard[String]("100")
    res34: String = String card: 100
    scala> getCard[Int](100)
    res35: String = int card: 100
    scala> getCard(100)
    res36: String = int card: 100

    上下边界Bounds

    在指定泛型类型的时候,有时候我们需要对泛型类型的范围进行界定,而不是可以任意的类型。

    Scala的上边界特性限制类型必须是某个类本身或其子类

    // 案例:在派对上交朋友
    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 Worker(val name: String)
    // 定义泛型上边界,语法为 T <: 类,表示必须要是该类或是其子类
    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 Worker
    defined class Party
    // 测试两个Person的子类
    scala> val leo = new Student("leo")
    leo: Student = Student@288b73c1
    scala> val spark = new Student("Sparks")
    spark: Student = Student@57df09a7
    scala> val party = new Party(leo, spark)
    party: Party[Student] = Party@998fbd4
    scala> party.play
    Hello, I'm leo
    Hello, I'm Sparks
    // 用一个不是Person子类的Worker类来演示错误
    scala> val jack = new Worker("jack")
    jack: Worker = Worker@68229a6
    // 因为不是Person子类而报错
    scala> val party = new Party(leo, jack)
    <console>:16: error: inferred type arguments [Object] do not conform to class Par
    bounds [T <: Person]
    val party = new Party(leo, jack)
    ^
    <console>:16: error: type mismatch;
    found : Student
    required: T
    val party = new Party(leo, jack)
    ^
    <console>:16: error: type mismatch;
    found : Worker
    required: T
    val party = new Party(leo, jack)

    Scala的下边界特性限制类型必须是某个类本身或其父类,这里不多赘述。

    View Bounds

    上下边界Bounds,虽然可以让类型限制在父子关系的范围内,但是如果某个类与上下边界Bounds指定的父子类型没有任何关系,那么默认是肯定不能接受的。

    然而,View 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)}
    // 隐式转换,将小狗类似转换为Person
    implicit def dog2person(obj: Object):Person = if(obj.isInstanceOf[Dog]){
    val dog = obj.asInstanceOf[Dog]
    new Person(dog.name)} else Nil
    // 定义view bounds, 语法为 [ 泛型参数 <% 或 >% 目标类型]
    // 代表将该类型进行隐式转化后,在判断是否在上下边界范围内
    class Party [T <% Person](p1: T, p2: T)
    // 测试
    scala> val spark = new Student("sparks")
    spark: Student = Student@2a0ce342
    scala> val doggy = new Dog("doggy")
    doggy: Dog = Dog@dcdb883
    // 通过隐式转换后,小狗也可以参加party
    scala> val party = new Party(spark, doggy)
    party: Party[Object] = Party@19de32cb

    Context Bounds(可删除)

    Context Bounds是一种特殊的Bounds,它会根据泛型类型的声明,比如”T:类型”要求必须存在一个类型为”类型[T]”的隐式值。

    其实个人认为,Context Bounds之所以叫Context,是因为它基于的是一种全局的上下文,需要利用到上下文的隐式值以及注入。

    // 使用Scala内置的比较器比较大小
    class Calculator[T: Ordering] (val number1: T, val number2: T){
    def max(implicit order: Ordering[T]) = if(order.compare(number1, number2) > 0) number1 else number
    2
    }
    defined class Calculator
    scala> val cal = new Calculator(1, 2)
    cal: Calculator[Int] = Calculator@7ae42ce3
    scala> cal.max
    res0: Int = 2

    Manifest Context Bounds

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

    // 案例:打包饭菜(一种食品达成一包)
    class Meat(val name: String)
    class Vegetable(val name: String)
    // 定义了一个泛型数组,[T:Manifest]
    def packageFood[T: Manifest] (food: T*) = {
    val foodPackage = new Array[T](food.length)
    for (i <- 0 until food.length) foodPackage(i) = food(i)
    foodPackage
    }
    defined class Meat
    defined class Vegetable
    packageFood: [T](food: T*)(implicit evidence$1: Manifest[T])Array[T]
    // 测试
    scala> val m1 = new Meat("niu rou")
    m1: Meat = Meat@1e63d216
    scala> val m2 = new Meat("yang rou")
    m2: Meat = Meat@22d9c961
    scala> val m3 = new Meat("zhu rou")
    m3: Meat = Meat@1e6cc850
    // 将三种肉类打成一包
    scala> packageFood(m1, m2, m3)
    res1: Array[Meat] = Array(Meat@49aa766b, Meat@22d9c961, Meat@1e6cc850)

    协变和逆变

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

    协变

    语法为:[+泛型参数]可以让类Card[Professionnal]成为类Card[Master]的子类

    // 案例:进入会场
    class Master
    class Professional extends Master
    // 我们希望大师以及大师级别以下的名片都可以进入会场
    // [+泛型]即为协变,可以让类Card[Professionnal]成为类Card[Master]的子类
    class Card[+T] (val name: String)
    def enterMeet(card: Card[Master]){
    println("Welcome to have this meeting!")
    }
    defined class Master
    defined class Professional
    defined class Card
    enterMeet: (card: Card[Master])Unit
    // 测试
    scala> val spark = new Card[Master]("sparks")
    spark: Card[Master] = Card@730f9695
    scala> val leo = new Card[Professional]("leo")
    leo: Card[Professional] = Card@4c6007fb
    scala> enterMeet(spark)
    Welcome to have this meeting!
    // 不仅大师可以进,专家也可以进入会场
    scala> enterMeet(leo)
    Welcome to have this meeting!

    逆变

    语法为:[-泛型参数]可以让类Card[Professionnal]成为类Card[Master]的父类

    // 案例:进入会场
    class Master
    class Professional extends Master
    /*
    我们希望专家级别的名片就可以进入会场,如果大师级别的名片,那就更欢迎了
    这里实际上需要将父类参数传递给子类泛型,默认不可以,需要进行强制类型转换才可以。但是逆变可以让我们轻松实现这一操作。
    [-泛型]即为逆变,可以让类Card[Professionnal]成为类Card[Master]的父类
    */
    class Card[-T] (val name: String)
    def enterMeet(card: Card[Professional]){
    println("welcome to have this meeting!")
    }
    // 测试
    scala> val sparks = new Card[Master]("sparks")
    sparks: Card[Master] = Card@4cc36c19
    scala> val leo = new Card[Professional]("leo")
    leo: Card[Professional] = Card@529c2a9a
    scala> enterMeet(leo)
    welcome to have this meeting!
    // 不仅专家可以进入,大师也可以进入!
    scala> enterMeet(sparks)
    welcome to have this meeting!

    Existential Type

    在Scala中,有一种特殊的类型参数,就是Existential Type,存在性类型。就是可以用_代替某种泛型参数,很简单但很重要,因为在Spark源码中随处可见。

    Array[T] forSome { type T}
    // 相当于
    Array[_] for Somw { type _ }
  • 相关阅读:
    每日英语:Apple's Latest iPhone Puts Focus Back on Fingerprint Security
    每日英语:Stressed at Work? Reflect on the Positive
    每日英语:Hold On: Reasons For Never Giving Up Your Dream
    每日英语:China's New Anti-Graft Website: A Tale of Tigers, Flies and Bath Tubs
    每日英语:China Overtakes U.S. in Number of Diabetes Cases
    每日英语:New Reason To Get The Kids To Bed On Time
    每日英语:Why Are Items Pricier in China?
    每日英语:Burning Question / Does Reading In Dim Light Hurt Your Eyes?
    每日英语:Genetic Manipulation Extends Life of Mice 20%
    架构设计之Spring-Session分布式集群会话管理
  • 原文地址:https://www.cnblogs.com/LiCheng-/p/8082678.html
Copyright © 2011-2022 走看看