好程序员大数据学习路线分享Scala系列之泛型,带有一个或多个类型参数的类是泛型的。
泛型类的定义:
//带有类型参数A的类定义 class Stack[A] { private var elements: List[A] = Nil //泛型方法 def push(x: A) { elements = x :: elements } def peek: A = elements.head def pop(): A = { val currentTop = peek elements = elements.tail currentTop } }
泛型类的使用,用具体的类型代替类型参数A。
val stack = new Stack[Int] stack.push(1) stack.push(2) println(stack.pop) // prints 2 println(stack.pop) // prints 1
1.协变 定义一个类型List[+A],如果A是协变的,意思是:对类型A和B,A是B的子类型,那么List[A]是List[B]的子类型。
abstract class Animal { def name: String } case class Cat(name: String) extends Animal case class Dog(name: String) extends Animal
Scala标准库有一个泛型类sealed abstract class List[+A],因为其中的类型参数是协变的,那么下面的程序调用时成功的。
object CovarianceTest extends App { //定义参数类型List[Animal] def printAnimalNames(animals: List[Animal]): Unit = { animals.foreach { animal => println(animal.name) } }
val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) //传入参数类型为List[Cat] printAnimalNames(cats) // Whiskers // Tom //传入参数类型为List[Dog] printAnimalNames(dogs) // Fido // Rex }
2.逆变 定义一个类型Writer[-A],如果A是逆变的,意思是:对类型A和B,A是B的子类型,那么Writer[B]是Writer[A]的子类型。
abstract class Animal { def name: String } case class Cat(name: String) extends Animal case class Dog(name: String) extends Animal
定义对应上述类进行操作的打印信息类
abstract class Printer[-A] { def print(value: A): Unit } class AnimalPrinter extends Printer[Animal] { def print(animal: Animal): Unit = println("The animal's name is: " + animal.name) }
class CatPrinter extends Printer[Cat] { def print(cat: Cat): Unit = println("The cat's name is: " + cat.name) }
逆变的测试
object ContravarianceTest extends App { val myCat: Cat = Cat("Boots")
//定义参数类型为Printer[Cat] def printMyCat(printer: Printer[Cat]): Unit = { printer.print(myCat) }
val catPrinter: Printer[Cat] = new CatPrinter val animalPrinter: Printer[Animal] = new AnimalPrinter
printMyCat(catPrinter) //可以传入参数类型为Printer[Animal] printMyCat(animalPrinter) }
3.上界 上界定义: T <: A ,表示类型变量T 必须是 类型A 子类
abstract class Animal { def name: String }
abstract class Pet extends Animal {}
class Cat extends Pet { override def name: String = "Cat" }
class Dog extends Pet { override def name: String = "Dog" }
class Lion extends Animal { override def name: String = "Lion" } //参数类型须是Pet类型的子类 class PetContainer[P <: Pet](p: P) { def pet: P = p } //Dog是Pet类型的子类 val dogContainer = new PetContainer[Dog](new Dog) //Cat是Pet类型的子类 val catContainer = new PetContainer[Cat](new Cat) //Lion不是Pet类型的子类,编译通不过 // val lionContainer = new PetContainer[Lion](new Lion)
4.下界 语法 B >: A 表示参数类型或抽象类型 B 须是类型A的父类。通常,A是类的类型参数,B是方法的类型参数。
上面这段代码,因为作为协变类型的B,出现在需要逆变类型的函数参数中,导致编译不通过。解决这个问题,就需要用到下界的概念。
trait Node[+B] { def prepend[U >: B](elem: U): Node[U] }
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) def head: B = h def tail: Node[B] = t }
case class Nil+B extends Node[B] { def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this) }
测试
trait Bird case class AfricanSwallow() extends Bird case class EuropeanSwallow() extends Bird
val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil()) val birdList: Node[Bird] = africanSwallowList birdList.prepend(new EuropeanSwallow)
5 视界(view bounds) 注意:已过时,了解即可
视界定义: A <% B ,表示类型变量A 必须是 类型B`的子类,或者A能够隐式转换到B
class Pair_Int[T <% Comparable[T]] (val first: T, val second: T){ def bigger = if(first.compareTo(second) > 0) first else second }
class Pair_Better[T <% Ordered[T]](val first: T, val second: T){ def smaller = if(first < second) first else second } object View_Bound {
def main(args: Array[String]) { // 因为Pair[String] 是Comparable[T]的子类型, 所以String有compareTo方法 val pair = new Pair_Int("Spark", "Hadoop"); println(pair.bigger)
/**
* Scala语言里 Int类型没有实现Comparable;
* 那么该如何解决这个问题那;
* 在scala里 RichInt实现了Comparable, 如果我们把int转换为RichInt类型就可以这样实例化了.
* 在scala里 <% 就起这个作用, 需要修改Pair里的 <: 为<% 把T类型隐身转换为Comparable[Int]
* String可以被转换为RichString. 而RichString是Ordered[String] 的子类.
*/
val pair_int = new Pair_Int(3 ,45)
println(pair_int.bigger)
val pair_better = new Pair_Better(39 ,5)
println(pair_better.smaller)
}
}
6 上下文界定(context bounds) 上下文界定的形式为 T : M, 其中M 必须为泛型类, 必须存在一个M[T]的隐式值.
class Pair_Context[T : Ordering](val first: T, val second: T){ def smaller(implicit ord: Ordering[T]) = if(ord.compare(first, second) < 0) first else second }
object Context_Bound {
def main(args: Array[String]) {
val pair = new Pair_Context("Spark", "Hadoop")
println(pair.smaller)
val int = new Pair_Context(3, 5)
println(int.smaller)
}
}