ITPub博客

首页 > 大数据 > 数据挖掘 > 好程序员大数据学习路线分享Scala系列之泛型

好程序员大数据学习路线分享Scala系列之泛型

原创 数据挖掘 作者:好程序员IT 时间:2019-10-10 15:56:05 0 删除 编辑

  好程序员大数据学习路线分享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)

  }

}

 


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69913892/viewspace-2659377/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
欢迎关注公众号:好程序员特训营 web前端教程分享 723729549 大数据教程分享 703503210

注册时间:2019-03-20

  • 博文量
    539
  • 访问量
    410779