scala学习复习笔记超详细(泛型和上下界)

Scala泛型和上下界

1. Scala泛型

  1. 如果我们要求函数的参数可以接受任意类型。可以使用泛型,这个类型可以代表任意的数据类型。
  2. 例如 List,在创建 List 时,可以传入整型、字符串、浮点数等等任意类型。那是因为 List 在 类定义时引用了泛型。比如在Java中:public interface List<E> extends Collection<E>
Scala泛型案例一
  1. 编写一个Message类
  2. 可以构建Int类型的Message,也可以构建String类型的Message。
  3. 要求使用泛型来完成设计(说明:不能使用Any)
/**
  * @Date 2021/4/5 18:25
  * @Version 10.21
  * @Author DuanChaojie
  */
object GenericDemo01 {
  def main(args: Array[String]): Unit = {
    val message1 = new Message[Int](10)
    val message2 = new Message[String]("str")
    println(message1.toString())
    println(message2.toString())
  }
}

class Message[T](inMes: T) {
  var mes = inMes
  override def toString = {
    "重写的toString方法...." + mes
  }
}
Scala泛型案例二
  1. 请设计一个EnglishClass (英语班级类),在创建EnglishClass的一个实例时,需要指定[ 班级开班季节(spring,autumn,summer,winter)、班级名称、班级类型]
  2. 开班季节只能是指定的,班级名称为String,班级类型是(字符串类型 “高级班”, “中级班”, “初级班”) 或者是 Int 类型(1, 2, 3 等)
  3. 请使用泛型来完成本案例
  4. Scala枚举详解…
package com.atguigu.chapter16

import com.atguigu.chapter16.SeasonEnum.SeasonEnum

/**
  * @Date 2021/4/5 18:32
  * @Version 10.21
  * @Author DuanChaojie
  */
object GenericDemo02 {
  def main(args: Array[String]): Unit = {
    val scalaClass = new EnglishClass[String, Int, String]("春天", 4, "Scala班")
    println(scalaClass.toString())

    val javaClass = new EnglishClass[SeasonEnum, String, String](SeasonEnum.spring, "Java", "5班")
    println(javaClass.toString)
  }
}

class EnglishClass[A, B, C](inSeason: A, inClassName: B, inClassType: C) {
  var season: A = inSeason
  var className: B = inClassName
  var classType: C = inClassType

  override def toString: String = {
    s"season = $season\nclassType = $classType\nclassName = $className 班"
  }
}

/**
  * 枚举类SeasonEnum
  */
object SeasonEnum extends Enumeration {
  type SeasonEnum = Value
  val spring, summer, autumn, winter = Value
}
Scala泛型案例三
  1. 定义一个函数,可以获取各种类型的 List 的中间index的值
  2. 使用泛型完成
/**
  * @Date 2021/4/5 19:21
  * @Version 10.21
  * @Author DuanChaojie
  */
object GenericDemo03 {
  def main(args: Array[String]): Unit = {
    val list1 = List[String]("dd", "ai", "mm")
    val list2 = List[Int](1, 2, 3)
    println(midValue(list1))
    println(midValue(list2))
  }

  def midValue[T](list: List[T]): T = {
    list(list.length / 2)
  }
}

2. Scala类型约束

测试上下界用到的公共类
package com.atguigu.chapter16

/**
  * @Date 2021/4/5 20:03
  * @Version 10.21
  * @Author DuanChaojie
  */
object BoundsCommonClass {
  // Scala上界和下界测试用到的类
}

/**
  * Earth 类
  */
class Earth {
  def sound() {
    println("hello Earth !")
  }
}

/**
  * Animal类
  */
class Animal extends Earth {
  //重写了Earth的方法sound()
  override def sound() = {
    println("animal sound")
  }
}

/**
  * Bird类
  */
class Bird extends Animal {
  // 将Animal的方法重写
  override def sound() = {
    println("bird sounds")
  }
}

class Moon{
  def sound() = {
    println("Moon Moon ~")
  }
}

class MyCat(val name: String, val age: Int) {
  override def toString: String = {
    s"name = $name \nage = $age"
  }
}
上界(Upper Bounds)

==Java中上界:==在 Java 泛型里表示某个类型是 A 类型的子类型,使用 extends 关键字,这种形式叫 upper bounds(上限或上界),语法如下:

<T extends A>
//或用通配符的形式:
<? extends A>

==Scala中上界:==在 Scala 里表示某个类型是 A 类型的子类型,也称上界或上限,使用 <: 关键字,语法如下:

[T <: A]
//或用通配符:
[_ <: A]

Scala上界应用案例:

  1. 编写一个通用的类,可以进行Int之间、Float之间、等实现了Comparable接口的值直接的比较。
  2. 分别使用传统方法和上界的方式来完成,体会上界使用的好处。
    1. 传统方法:class MyCompare1(inX: Int, inY: Int){...}
    2. 使用上界:class MyCompare2[T <: Comparable[T]](inX: T, inY: T) {...}
package com.atguigu.chapter16

/**
  * @Date 2021/4/5 19:27
  * @Version 10.21
  * @Author DuanChaojie
  */
object UpperBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    val myCompare1 = new MyCompare1(8, 9)
    println(myCompare1.max)

    /**
      * 注意Scala中Int不是Comparable的子类
      */
    val myCompare2 = new MyCompare2[Integer](8, 9)
    println(myCompare2.max)
    val myCompare22 = new MyCompare2[java.lang.Float](8.4f, 8.6f)
    println(myCompare22.max)
  }
}

/**
  * 传统方式解决
  *
  * @param inX
  * @param inY
  */
class MyCompare1(inX: Int, inY: Int) {
  var x = inX
  var y = inY

  def max = if (x > y) x else y
}

/**
  * 使用Scala上界来完成
  * 1. [T <: Comparable[T]] 表示T类型是Comparable 子类型
  * 2. 即你传入的T类要继承Comparable接口
  * 3. 这样就可以使用compareTo方法
  * 4. 这样的写法(使用上界的写法)通用性比传统的好
  */
class MyCompare2[T <: Comparable[T]](inX: T, inY: T) {
  var x = inX
  var y = inY

  /**
    * x.compareTo(y) 将此对象与指定对象进行比较。
    * 负数--->  x < y
    * 0   --->  x == y
    * 正数--->  x > y
    */
  def max = if (x.compareTo(y) > 0) x else y
}

Scala中上界课程测试题(理解上界含义)

  • 案例中使用到的类,见BoundsCommonClass.scala
package com.atguigu.chapter16

/**
  * @Date 2021/4/5 19:49
  * @Version 10.21
  * @Author DuanChaojie
  */
object UpperBoundsDemo02 {
  def main(args: Array[String]): Unit = {
    getSound(Seq(new Bird,new Bird))
    println("-----------")
    getSound(Seq(new Animal,new Animal))
    println("-----------")
    getSound(Seq(new Bird,new Animal))
    println("-----------")
    //getSound(Seq(new Earth,new Earth)) 报错,因为Earth不是Animal的子类
    //getSound(Seq(new Moon)) 报错
  }

  def getSound[T <: Animal](seq: Seq[T]) = {
    seq.map(_.sound())
  }
}
下界(lower bounds)

==Java中下界:==在 Java 泛型里表示某个类型是 A类型的父类型,使用 super 关键字,语法如下:

<T super A>
//或用通配符的形式:
<? super A>

==Scala中下界:==在 Scala 的下界或下限,使用 >: 关键字,语法如下:

[T >: A]
//或用通配符:
[_ >: A]

Scala中下界应用实例

/**
  * @Date 2021/4/5 20:01
  * @Version 10.21
  * @Author DuanChaojie
  */
object LowerBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    getSound(Seq(new Earth,new Earth)).map(_.sound())
    println("-------------------")
    getSound(Seq(new Animal,new Animal)).map(_.sound())
    println("-------------------")
    // 可以使用,而且打印的是bird sounds,原因见小结
    getSound(Seq(new Bird,new Bird)).map(_.sound())
    println("-------------------")
    val seq = getSound(Seq(new Moon)) // seq是Object类型的
    // val moon = seq.asInstanceOf[Moon] 报错
    // moon.sound()
  }

  def getSound[T >: Animal](seq: Seq[T]) = seq
}

Scala中下界的使用小结:

  1. 对于下界,可以传入任意类型。
  2. 传入和Animal直系的,是Animal父类的还是父类处理,是Animal子类的按照Animal处理。
  3. 和Animal无关的,一律按照Object处理。
  4. 也就是下界,可以随便传,只是处理是方式不一样。
  5. 不能使用上界的思路来类推下界的含义。
视图界定
  1. <% 的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换类型。
  2. 视图界定应用案例:
  3. 使用视图界定的方式,比较两个Person对象的年龄大小。
/**
  * @Date 2021/4/5 20:16
  * @Version 10.21
  * @Author DuanChaojie
  */
object ViewBoundsDemo01 {

  //  隐士函数
  implicit def MyImplicit(cat: MyCat) = new Ordered[MyCat] {
    override def compare(that: MyCat) = {
      cat.age - that.age
    }
  }


  def main(args: Array[String]): Unit = {
    val tom = new MyCat("tom", 18)
    val jack = new MyCat("jack", 2)

    val myCompare3 = new MyCompare3(tom, jack)
    println(myCompare3.max)
  }
}



/**
  * Ordered和Ordering的区别?
  * Ordering中的方法:def compare(x: T, y: T): Int
  * Orderd中有这个方法:  def compareTo(that: A): Int = compare(that)
  * 1.  T <% Ordered[T] 表示T是Ordered子类型 java.lang.Comparable
  * 2.  这里调用的compareTo方法是 T这个类型的方法
  */
class MyCompare3[T <% Ordered[T]](x: T, y: T) {
  def max = if (x.compareTo(y) > 0) x else y
}
上下文界定
  1. 与 view bounds 一样 context bounds(上下文界定)也是隐式参数的语法糖。为语法上的方便, 引入了”上下文界定”这个概念。
  2. 上下文界定应用实例
    1. 使用上下文界定+隐式参数的方式,比较两个Person对象的年龄大小
    2. 使用Ordering实现比较。
package com.atguigu.chapter16

/**
  * @Date 2021/4/5 20:53
  * @Version 10.21
  * @Author DuanChaojie
  */
object ContextBoundsDemo01 {
  /**
    * 这里我定义一个隐式值  Ordering[MyCat]类型
    */
  implicit val catComparetor = new Ordering[MyCat] {
    override def compare(cat1: MyCat, cat2: MyCat): Int =
      cat1.age - cat2.age
  }

  def main(args: Array[String]): Unit = {
    val tom = new MyCat("tom", 18)
    val jack = new MyCat("jack", 2)
    val myCompare4 = new MyCompare4(tom, jack)
    println(myCompare4.max)

    println("-----------------")
    val myCompare5 = new MyCompare5(tom, jack)
    println(myCompare5.max)
    println("-----------------")
    val myCompare6 = new MyCompare6(tom, jack)
    println(myCompare6.max)
  }
}


/**
  * 方式1
  * 1. [T: Ordering] 泛型
  * 2. o1: T, o2: T 接受T类型的对象
  * 3. implicit comparetor: Ordering[T] 是一个隐式参数
  */
class MyCompare4[T: Ordering](o1: T, o2: T)(implicit comparetor: Ordering[T]) {
  def max = if (comparetor.compare(o1, o2) > 0) o1 else o2
}

/**
  * 方式2,将隐式参数放到方法内
  */
class MyCompare5[T: Ordering](o1: T, o2: T) {
  def max = {
    //返回一个数字
    def fun(implicit comparetor: Ordering[T]) = comparetor.compare(o1, o2)
    //如果f1返回的值>0,就返回o1,否则返回o2
    if (fun > 0) o1 else o2
  }

  def lowwer = {
    def f1(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2) //返回一个数字
    //如果f1返回的值>0,就返回o1,否则返回o2
    if (f1 > 0) o2 else o1
  }
}

/**
  * 方式3,使用implicitly语法糖,最简单(推荐使用)
  */
class MyCompare6[T: Ordering](o1: T, o2: T) {
  def max = {
    /**
      * 这句话就是会发生隐式转换,获取到隐式值 personComparetor
      * 底层仍然使用编译器来完成绑定(赋值的)工作
      */
    val comparetor = implicitly[Ordering[T]]
    //println("comparetor hashcode=" + comparetor.hashCode())
    if (comparetor.compare(o1, o2) > 0) o1 else o2
  }
}

3. Scala协变、逆变和不变

  1. Scala的covariant协变(+),contravariant逆变(-),不可变invariant。
  2. 在Java里,泛型类型都是invariant,比如 List 并不是 List 的子类型。而Scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),如: trait List[+T] // 在类型定义时声明为协变这样会把List[String]作为List[Any]的子类型。
应用实例

在这里引入关于这个符号的说明,在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变

  1. C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变
  2. C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变
  3. C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变。
package com.atguigu.chapter16

/**
  * @Date 2021/4/5 21:13
  * @Version 10.21
  * @Author DuanChaojie
  */
object LastDemo01 {
  /**
    * C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变 
    * C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变 
    * C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变
    */
  def main(args: Array[String]): Unit = {

    val t4: Temp4[Super] = new Temp4[Sub]("hello"); //协变

    val t5: Temp5[Sub] = new Temp5[Super]("hello"); //逆变

    val t6: Temp6[Sub] = new Temp6[Sub]("hello"); //不变

  }
}

//协变
class Temp4[+A](title: String) {
  override def toString: String = {
    title
  }
}

//逆变
class Temp5[-A](title: String) {
  override def toString: String = {
    title
  }
}

//不变
class Temp6[A](title: String) {
  override def toString: String = {
    title
  }
}

// 支持协变
class Super

//Sub是Super的子类
class Sub extends Super

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值