10-scala的泛型

89 阅读1分钟

泛型基础

我们利用[]来说明泛型。给出代码实例:

package example

class Stack[A] {
  private var elements: List[A] = Nil

  def push(elem: A) = 
    elements = elem :: elements
  
  def peek() = elements.head

  def pop(): A = {
    val top = peek
    elements = elements.tail
    top
  }
}

object MyExample {

  def main(args: Array[String]): Unit = {
    val st = new Stack[Int]
    st.push(10)
    st.push(20)
    println(st.peek)
    st.pop()
    println(st.peek)
  }
}

代码输出:

20
10

型变

协变

看下定义: 如果BA的类,那么List[B]就是List[A]的子类。在泛型中,我们需要定义为List[+A]

直接给出代码实例:

package example

abstract class Animal

class Cat(val catName: String) extends Animal

class Dog(val dogName: String) extends Animal

// 这里声明为协变
abstract class AnimalPrinter[+Animal] {
  def print(): Unit
}

class CatPrinter(name: String) extends AnimalPrinter[Cat] {
  private val cat = new Cat(name)
  override def print() = println(cat.catName)
}

class DogPrinter(name: String) extends AnimalPrinter[Dog] {
  private val dog = new Dog(name)
  override def print() = println(dog.dogName)
}

object MyExample {
  // 返回值这里,前面我们声明为协变类型了,因此这里可以直接返回,否则会报错
  def getPrinter(n: Int, name: String): AnimalPrinter[Animal] = {
    if (n < 0) {
      new CatPrinter(name)
    } else {
      new DogPrinter(name)
    }
  }

  def main(args: Array[String]): Unit = {
    val catPrinter = getPrinter(-1, "foo-cat")
    val dogPrinter = getPrinter(1, "foo-dog")
    catPrinter.print()
    dogPrinter.print()
  }
}

代码输出:

foo-cat
foo-dog

我们在代码中,把AnimalPrinter声明为Animal协变的泛型类型,因此只要其他类A集成了AnimalPrinter而且泛型也是Animal的子类,那么A就是AnimalPrinter[Animal]的子类,面向对象的那些法则都可以适用。协变还是比较容易理解的。

逆变

看下定义:

逆变正好和协变相反,我们需要看下适用应用来理解对应的场景:

package example

abstract class Fruit {

  def getPrice(): Double
  
  def getName(): String
}

class Apple(name: String, var price: Double) extends Fruit {
  override def getName(): String = name

  override def getPrice(): Double = price

  override def toString(): String = s"[name: $name, price $price]"
}

// 这里声明为逆变
trait Judger[-T] {
  def judge(t: T): Boolean
}

class PriceJudger(val price) extends Judger[Fruit] {
  override def judge(fruit: Fruit): Boolean = fruit.getPrice() < price
}

class AppleBasket {
  private var apples: List[Apple] = Nil
  
  def addApple(apple: Apple): Unit = {
    apples = apple :: apples
  }

  def getCheapApples(judger: Judger[Apple]): List[Apple] = {
    apples.filter(judger.judge(_)).toList
  }
}

object MyExample {

  def main(args: Array[String]): Unit = {
    val appleBasket = new AppleBasket
    appleBasket.addApple(new Apple("a0", 5.2))
    appleBasket.addApple(new Apple("a1", 11.8))
    appleBasket.addApple(new Apple("a2", 7.9))

    val priceJudger = new PriceJudger(10.0)
    // 这里用到了逆变的特性,priceJudger反而是Judger[Fruit]的子类的了
    val cheapApples = appleBasket.getCheapApples(priceJudger)
    println(cheapApples.toString)
  }
}

代码输出:

List([name: a2, price 7.9], [name: a0, price 5.2])

我们说明一下代码的意义:

  • Fruit作为抽象水果类型,拥有抽象方法getNamegetPriceApple是具体化的水果class

  • Judge是一个泛型的trait类型,拥有judge抽象方法方法,一般用于判断某个类型的实例是否满足要求。这里,我们具体化了一个PriceTrait类,该类可以判断所有种类的水果,价格是否符合预期。

  • AppleBasket是一个苹果集合,我们有addApplegetCheapApples方法。后者是获取价格低于10的苹果种类List对于AppleBasket实现者来说,我们需要的就是针对Apple判断价格类型的Judger即可,因此参数是Judger[Apple]类型

  • 但是,站在系统架构角度来看,价格Judger只需要有一个class即可,然后根据实际情况设置阈值

综上所述,我们给Judger设置为协变的泛型,这样Judger[Fruit]Judger[Apple]的子类的了,自然就可以了完成上述代码中的赋值了。

协变一般是为了各类架构设计者和具体代码实现者的一种方案,然后简化写的代码量。

类型上下界

上界

规定泛型类型中,必须是某个类或者它的子类。直接给出代码:

package example

abstract class Animal

class Cat extends Animal

class Dog extends Animal

class Apple

class AnimalContainer[T <: Animal] {
  private var animals: List[T] = Nil

  def addAnimal(animal: T) = animals = animal :: animals
}

object MyExample {

  def main(args: Array[String]): Unit = {
    val catContainer = new AnimalContainer[Cat]
    val dogContainer = new AnimalContainer[Dog]
    // 错误的,必须是Animal子类
    // val appleContainer = new AnimalContainer[Apple]
  }
}

下界

和上界相反,必须是某个类或者该类的父类,直接给出代码实例:

package example

abstract class Animal

class Dog extends Animal

class SmallDog extends Dog

class MiddleDog extends SmallDog

class BigDog extends MiddleDog

class DogContainer[T >: MiddleDog] {

}

object MyExample {

  def main(args: Array[String]): Unit = {
    val dc = new DogContainer[MiddleDog]
    val dc1 = new DogContainer[SmallDog]
    // 编译错误,被类型限制了
    // val dc2 = new DogContainer[BigDog]
  }
}