Scala-泛型

   Scala语言是由Martin Odersky开发,Odersky之前主要负责Java语言的泛型的开发,可想而知,泛型模块在Scala语言中还是比较重要的。

1.泛型基础

  • 泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性。
  • 泛型分类为:
    • 泛型类-- 泛型作用在类上
    • 泛型方法-- 泛型作用在方法上
  • Scala的泛型类的书写,有点类似于Java中泛型类的书写规则,但是注意符号等细节问题
package cn.mikeal.generic

// 定义泛型类
class Person[T](var name: T)

class Student[T, S](var name: T, var age: S)

object Test2 {

  def main(args: Array[String]): Unit = {
    val p: Person[String] = new Person[String]("张三")
    println(p.name)

    val stu1: Student[String, Int] = new Student[String, Int]("李四", 18)
    println(stu1.name + "\t" + stu1.age)
  }

}

​
  • Scala的方法的书写
package cn.mikeal.generic

/**
  * 泛型方法的定义
  */
object Test5 {

  def add[T](x: Int, y: Int): Int = {
    x + y
  }

  def main(args: Array[String]): Unit = {
    println(add[Int](12,14))
    // println(add[Int](12.4,14))
  }

}

2.类型变量界定

  • 类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩小泛型的具体范围
  • 如下所示定义的scala 类编译期报错
class GenericTypeTest1 {
  def compare[T](first: T, second: T) = {
    if (first.compareTo(second) > 0)
      first
    else
      second
  }
}
  • 需要修改代码如下所示:  -- 添加变量界定
package cn.mikeal.generic

object Test1 {

  def main(args: Array[String]): Unit = {
    val tvb = new GenericTypeTest2
    println(tvb.compare("A", "B"))
  }

}

class GenericTypeTest2 {
  def compare[T <: Comparable[T]](first: T, second: T) = {
    if (first.compareTo(second) > 0)
      first
    else
      second
  }
}
  • 总结:
    • <: 类型界定 表示传入的泛型类是属于继承自Comparable[T]的,才会存在compareTo方法
    • 因为如果不采用类型界定,则该类不一定存在compareTo方法,所以编译期间报错

3.视图界定

  • 如上所示,类型变量界定是建立在继承的关系之上的,但有时候这种限定不能满足实际要求,如果希望跨越类继承层次结构时,可以使用视图界定来实现的,其后面的原理是通过隐式转换来实现。视图界定利用<%符号来实现。
  • 如下代码运行会抛出异常
package cn.mikeal.generic

object Test2 {

  def main(args: Array[String]): Unit = {
    /**
      * 抛出异常,因为Int类型没有继承Comparable类
      */
    val s1 = new Student1("张三", 11)
    val s2 = new Student1("张三", 11)
  }

}

case class Student1[T, S <: Comparable[S]](var name: T, var height: S)
  • 修改泛型为视图界定
package cn.mikeal.generic

object Test3 {

  def main(args: Array[String]): Unit = {
    val s1 = new Student2("张三", 11)
    val s2 = new Student2("张三", 11)
  }

}

case class Student2[T, S <% Comparable[S]](var name: T, var height: S)
  • 总结:利用<%符号对泛型 S 进行限定,它的意思是 S 可以是 Comparable 类继承层次结构中实现了Comparable 接口的类,也可以是能够经过隐式转换得到的实现了 Comparable 接口的类。

4.上界和下界

  1. 上界符号为 <: 指定某个类本类及其所有继承类
  2. 下界符号为 >: 是限制了最底层的类型,指定类型必须是该类本身及其所有的父类
    1. 下界的作用主要是保证类型安全

5.逆变和协变

5.1 协变

  1. 协变定义形式如:trait List[+T]{} -- 当类型 B 是类型 A 的子类型时,则 List[B]也可以认为是 List[A}的子类型,即 List[B]可以泛化为 List[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变(covariance)
  2. Java中不存在协变
  3. 协变的代码实现
​
package cn.mikeal.generic

/**
  * Scala的协变
  */
object Test4 {

  def main(args: Array[String]): Unit = {

    var mylist1: MyList[People] = new MyList[People](new People("张三", 13))
    var mylist2: MyList[Student] = new MyList[Student](new Student("张三", 13, 1001))
    mylist1 = mylist2
  }

}

/**
  * [+T] -- 指定协变
  *
  * @param head
  * @tparam T
  */
class MyList[+T](val head: T) {}

class People(var name: String, var age: Int) {}

class Student(name: String, age: Int, var sid: Int) extends People(name, age) {}

class Reader(name: String, age: Int, var sex: String) extends People(name, age) {}

​

5.2 逆变

  1. 当类型 B 是类型 A 的子类型,则 Queue[A]反过来可以认为是 Queue[B}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance)
  2. 逆变的代码实现
package cn.mikeal.generic

/**
  * Scala的逆变
  */
object Test5 {

  def main(args: Array[String]): Unit = {

    var mylist1: MyList1[People1] = new MyList1[People1](new People1("张三", 13))
    var mylist2: MyList1[Student1] = new MyList1[Student1](new Student1("张三", 13, 1001))
    mylist2 = mylist1
  }

}

/**
  * [1T] -- 指定逆变
  *
  * @param head
  * @tparam T
  */
class MyList1[-T](val head: T) {}

class People1(var name: String, var age: Int) {}

class Student1(name: String, age: Int, var sid: Int) extends People1(name, age) {}

class Reader1(name: String, age: Int, var sex: String) extends People1(name, age) {}

猜你喜欢

转载自blog.csdn.net/qq_29362113/article/details/89479009