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.上界和下界
- 上界符号为 <: 指定某个类本类及其所有继承类
- 下界符号为 >: 是限制了最底层的类型,指定类型必须是该类本身及其所有的父类
- 下界的作用主要是保证类型安全
5.逆变和协变
5.1 协变
- 协变定义形式如:trait List[+T]{} -- 当类型 B 是类型 A 的子类型时,则 List[B]也可以认为是 List[A}的子类型,即 List[B]可以泛化为 List[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变(covariance)
- Java中不存在协变
- 协变的代码实现
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 逆变
- 当类型 B 是类型 A 的子类型,则 Queue[A]反过来可以认为是 Queue[B}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance)
- 逆变的代码实现
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) {}