尚硅谷Scala (18)

十八、泛型、上下界、视图界定 、上下文界定

18.1 泛型的基本介绍

18.1.1 基本介绍

1) 如果我们要求函数的参数可以接受任意类型。可以使用泛型,这个类型可以代表任意的数据类
型。
2) 例如 List ,在创建 List 时,可以传入整型、字符串、浮点数等等任意类型。那是因为 List
类定义时引用了泛型。比如在 Java 中: public interface List<E> extends Collection<E>

18.1.2 Scala 泛型应用案例 1

要求 :
1) 编写一个 Message
2) 可以构建 Int 类型的 Message,String 类型的 Message.
3) 要求使用泛型来完成设计 ,( 说明:不能使用 Any)
object GenericDemo01 {
  def main(args: Array[String]): Unit = {
    val intMessage = new IntMessage[Int](10)
    println(intMessage)
    val strMessage = new StringMessage[String]("hello")
    println(strMessage)
  }
}

/*
编写一个Message类
可以构建Int类型的Message,
String类型的Message.
要求使用泛型来完成设计,(说明:不能使用Any)
 */

abstract class Message[T](s:T) {
  def get = s
}

class IntMessage[Int](v:Int) extends Message(v)
class StringMessage[String](v:String) extends Message(v)

18.1.3 Scala 泛型应用案例 2

要求
1) 请设计一个 EnglishClass ( 英语班级类 ) ,在创建 EnglishClass 的一个实例时,需要指定 [ 班级开
班季节 (spring,autumn,summer,winter) 、班级名称、班级类型 ]
2) 开班季节只能是指定的,班级名称为 String, 班级类型是 ( 字符串类型 " 高级班 ", " 初级班 "..)
者是 Int 类型 (1, 2, 3 )
3) 请使用泛型来完成本案例 .
object GenericDemo02 {
  def main(args: Array[String]): Unit = {

    //使用
    val class01 = new EnglishClass[SeasonEnum.SeasonEnum,String,String](SeasonEnum.spring,"0705班","高级班")
    println("class01 " + class01.classSesaon + " " + class01.className + class01.classType)

    val class02 = new EnglishClass[SeasonEnum.SeasonEnum,String,Int](SeasonEnum.spring,"0707班",1)

    println("class02 " + class02.classSesaon + " " + class02.className + class02.classType)
  }
}

/*
Scala泛型应用案例2

要求
请设计一个EnglishClass (英语班级类),在创建EnglishClass的一个实例时,需要指定[ 班级开班季节(spring,autumn,summer,winter)、班级名称、班级类型]
开班季节只能是指定的,班级名称为String, 班级类型是(字符串类型 "高级班", "初级班"..) 或者是 Int 类型(1, 2, 3 等)
请使用泛型来完成本案例.

 */
class EnglishClass[A, B, C](val classSesaon: A, val className: B, val classType: C)

//季节是枚举类型
class SeasonEnum extends Enumeration {
  type SeasonEnum = Value
  val spring,autumn,summer,winter = Value
}

18.1.4 Scala 泛型应用案例 3

要求
1) 定义一个函数,可以获取各种类型的 List 的中间 index 的值
2) 使用泛型完成
object GenericDemo03 {
  def main(args: Array[String]): Unit = {
    val list1 = List("hello", "dog", "world")
    val list2 = List(90, 10, 23)
    println(midList[String](list1))// "dog"
    println(midList[Int](list2))// 10
  }

  /*
要求
定义一个函数,可以获取各种类型的 List 的中间index的值
使用泛型完成

 */
  def midList[E](l: List[E]): E = {
    l(l.length / 2)
  }
}

18.2 类型约束-上界(Upper Bounds)/下界(lower bounds)

18.2.1 上界(Upper Bounds)介绍和使用

java 中上界
Java 泛型里表示某个类型是 A 类型的子类型,使用 extends 关键字,这种形式叫 upper
bounds( 上限或上界 ) ,语法如下:
<T extends A>
// 或用通配符的形式:
<? extends A>

18.2.2 上界(Upper Bounds)介绍和使用

scala 中上界
scala 里表示某个类型是 A 类型的子类型,也称上界或上限,使用 <: 关键字,语法如下:
[T <: A]
// 或用通配符 :
[_ <: A]

18.2.3 上界(Upper Bounds)介绍和使用

scala 中上界应用案例 - 要求
1) 编写一个通用的类,可以进行 Int 之间、 Float 之间、等实现了 Comparable 接口的值直接的比
.//java.lang.Integer
2) 分别使用 传统方法 上界的方式 来完成,体会上界使用的好处 .
3) 代码
object UpperBoundsDemo01 {
  def main(args: Array[String]): Unit = {

    val compareInt = new CompareInt(10,40)
    println(compareInt.greater) // 40

    //第一个用法
    val commonCompare1 = new CommonCompare(Integer.valueOf(10), Integer.valueOf(40))//Int
    println(commonCompare1.greater)
    //第二个用法
    val commonCompare2 = new CommonCompare(java.lang.Float.valueOf(1.1f), java.lang.Float.valueOf(2.1f))//Fl
    println(commonCompare2.greater)

    //第3种写法使用了隐式转换
    //implicit def float2Float(x: Float): java.lang.Float         = x.asInstanceOf[java.lang.Float]
    val commonCompare3 = new CommonCompare[java.lang.Float](10.1f, 21.1f)//
    println(commonCompare3.greater)
  }

}

/*
编写一个通用的类,可以进行Int之间、Float之间、等实现了Comparable接口的值直接的比较.//java.lang.Integer
分别使用传统方法和上界的方式来完成,体会上界使用的好处.

 */
//传统方法
class CompareInt(n1: Int, n2: Int) {
  //返回较大的值
  def greater = if(n1 > n2) n1 else n2
}

//使用上界(上限)来完成
//说明
//1. [T <: Comparable[T]] 表示T类型是Comparable 子类型
//2. 即你传入的T类要继承Comparable接口
//3. 这样就可以使用compareTo方法
//4. 这样的写法(使用上界的写法)通用性比传统的好
class CommonCompare[T <: Comparable[T]](obj1:T,obj2:T) {
  def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

18.2.4 下界(Lower Bounds)介绍和使用

Java 中下界
Java 泛型里表示某个类型是 A 类型的父类型,使用 super 关键字
<T super A>
// 或用通配符的形式:
<? super A>
scala 中下界
scala 的下界或下限,使用 >: 关键字,语法如下:
[T >: A]
// 或用通配符 :
[_ >: A]
scala 中下界应用实例
//1)和Animal直系的,是Animal父类的还是父类处理,是Animal子类的按照Animal处理(),
//2)和Animal无关的,一律按照Object处理!
object LowerBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    println("ok!")
    //满足下界的约束
    biophony(Seq(new Earth, new Earth)).map(_.sound())
    //满足下界的约束
    biophony(Seq(new Animal, new Animal)).map(_.sound())

    //这里我们不能使用上界的思路去推导,这里是可以运行
    //1.?
    println("===================")
    biophony(Seq(new Bird, new Bird)).map(_.sound())//

    //biophony(Seq(new Moon))

  }
  //下界
  def biophony[T >: Animal](things: Seq[T]) = things
}


class Earth { //Earth 类
  def sound(){ //方法
    println("hello !")
  }
}
class Animal extends Earth{
  override def sound() ={ //重写了Earth的方法sound()
    println("animal sound")
  }
}
class Bird extends Animal{
  override def sound()={ //将Animal的方法重写
    print("bird sounds")
  }
}
class Moon {
//  def sound()={ //将Animal的方法重写
//    print("bird sounds")
//  }
}

scala 中下界的使用小结
def biophony[T >: Animal](things: Seq[T]) = things
1) 对于下界,可以传入任意类型
2) 传入和 Animal 直系的,是 Animal 父类的还是父类处理,是 Animal 子类的按照 Animal 处理
3) Animal 无关的,一律按照 Object 处理
4) 也就是下界,可以随便传,只是处理是方式不一样
5) 不能使用上界的思路来类推下界的含义

18.2.5 视图界定应用案例 3

说明 : 自己写 隐式转换 结合 视图界定 的方式,比较两个 Person 对象的年龄大小
object ViewBoundsDemo03 {
  def main(args: Array[String]): Unit = {
    val p1 = new Person3("汤姆", 13)
    val p2 = new Person3("杰克", 10)
    //引入隐式函数
    import MyImplicit._
    val compareComm3 = new CompareComm3(p1,p2)
    println(compareComm3.getter)

  }
}


class Person3(val name: String, val age: Int)  {
  //这里是重写toString,为了显示方便
  override def toString: String = this.name + "\t" + this.age
}


//说明
//1.  T <% Ordered[T] 表示T是Ordered子类型 java.lang.Comparable
//2.  这里调用的compareTo方法是 T这个类型的方法
class CompareComm3[T <% Ordered[T]](obj1: T, obj2: T) {
  def getter = if (obj1 > obj2) obj1 else obj2

  def geatter2 = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}
object MyImplicit {
  implicit def person3toOrderedPerson3(p3:Person3) = new Ordered[Person3] {
    override def compare(that: Person3) = { //是你自己的业务逻辑
      p3.age - that.age
    }
  }

}

18.3 类型约束-上下文界定(Context bounds)

18.3.1 基本介绍

view bounds 一样 context bounds( 上下文界定 ) 也是 隐式参数的语法糖 。为语法上的方便, 引入
了”上下文界定”这个概念

18.3.2 上下文界定应用实例

要求:使用上下文界定 + 隐式参数的方式,比较两个 Person 对象的年龄大小
要求:使用 Ordering 实现比较
代码
object ContextBoundsDemo {
  //这里我定义一个隐式值  Ordering[Person]类型
  implicit val personComparetor = new Ordering[Person4] {
    override def compare(p1: Person4, p2: Person4): Int =
      p1.age - p2.age
  }

  def main(args: Array[String]): Unit = {
    //
    val p1 = new Person4("mary", 30)
    val p2 = new Person4("smith", 35)
    val compareComm4 = new CompareComm4(p1, p2)
    println(compareComm4.geatter) // "smith", 35

    val compareComm5 = new CompareComm5(p1, p2)
    println(compareComm5.geatter) // "smith", 35

    println("personComparetor hashcode=" + personComparetor.hashCode())
    val compareComm6 = new CompareComm6(p1, p2)
    println(compareComm6.geatter) // "smith", 35

  }
}


//一个普通的Person类
class Person4(val name: String, val age: Int) {

  //重写toStirng
  override def toString = this.name + "\t" + this.age
}

//方式1
//说明:
//1. [T: Ordering] 泛型
//2. obj1: T, obj2: T 接受T类型的对象
//3. implicit comparetor: Ordering[T] 是一个隐式参数
class CompareComm4[T: Ordering](obj1: T, obj2: T)(implicit comparetor: Ordering[T]) {
  def geatter = if (comparetor.compare(obj1, obj2) > 0) obj1 else obj2
}

//方式2
//方式2,将隐式参数放到方法内
class CompareComm5[T: Ordering](o1: T, o2: T) {
  def geatter = {
    def f1(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2) //返回一个数字
    //如果f1返回的值>0,就返回o1,否则返回o2
    if (f1 > 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
//方式3,使用implicitly语法糖,最简单(推荐使用)
class CompareComm6[T: Ordering](o1: T, o2: T) {
  def geatter = {
    //这句话就是会发生隐式转换,获取到隐式值 personComparetor
    //底层仍然使用编译器来完成绑定(赋值的)工作
    val comparetor = implicitly[Ordering[T]]
    println("comparetor hashcode=" + comparetor.hashCode())
    if (comparetor.compare(o1, o2) > 0) o1 else o2
  }
}

18.4 协变、逆变和不变

18.4.1 基本介绍

1) Scala 的协变 (+) ,逆变 (-) ,协变 covariant 、逆变 contravariant 、不可变 invariant
2) 对于一个带类型参数的类型,比如 List[T] ,如果对 A 及其子类型 B ,满足 List[B] 也符合 List[A]
的子类型,那么就称为 covariance( 协变 ) ,如果 List[A] List[B] 的子类型,即与原来的父子关系正相反,则称为 contravariance( 逆变 ) 。如果一个类型支持协变或逆变,则称这个类型为 variance( 翻译为可变的或变型) ,否则称为 invariance( 不可变的 )
3) Java 里,泛型类型都是 invariant ,比如 List<String> 并不是 List<Object> 的子类型。而 scala 支持,可以在定义类型时声明( 用加号表示为协变,减号表示逆变 ) ,如 : trait List[+T] // 在类型定义时声明为协变这样会把 List[String] 作为 List[Any] 的子类型。

18.4.2 应用实例

在这里引入关于这个符号的说明,在声明 Scala 的泛型类型时,“ + ”表示协变,而“ - ”表示逆变
C[+T] :如果 A B 的子类,那么 C[A] C[B] 的子类,称为协变
C[-T] :如果 A B 的子类,那么 C[B] C[A] 的子类,称为逆变
C[T] :无论 A B 是什么关系, C[A] C[B] 没有从属关系。称为不变 .
代码
object Demo {
  def main(args: Array[String]): Unit = {
    val t1: Temp3[Sub] = new Temp3[Sub]("hello");
    //ok
    //      val t2: Temp3[Sub] = new Temp3[Super]("hello");//error
    //      val t3: Temp3[Super] = new Temp3[Sub]("hello");//error
    val t4: Temp3[Sub] = new Temp3[Sub]("hello"); //ok
    val t5: Temp4[Super] = new Temp4[Sub]("hello"); //ok
    //val t6: Temp4[Sub] = new Temp4[Super]("hello"); //ok
    val t7: Temp5[Sub] = new Temp5[Sub]("hello"); //ok
    val t8: Temp5[Sub] = new Temp5[Super]("hello"); //ok
    //val t9: Temp5[Super] = new Temp5[Sub]("hello"); //ok


  }
}


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

//不变
class Temp3[A](title: String) { //Temp3[+A] //Temp[-A]
  override def toString: String = {
    title
  }
}

//支持协变
class Super //父类

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值