22 泛型
泛型就是不确定得类型,可以在类或方法不确实传入类型时使用,可以提高代码得灵活性和复用性;
scala中泛型得用法和java中差不多,但是会有一些自己独特得语法;
泛型类:指定类可以接受任意类型参数。
泛型方法:指定方法可以接受任意类型参数。
22.1 泛型类基本用法#package day04import day04.SexEnumObj.SexEnum// 定义带有泛型得抽象类abstract class FXDemo[T](val t : T) { def printInfo():Unit}// 子类继承父类,把泛型具体化成Intclass FXIntDemo[Int](t : Int) extends FXDemo[Int](t:Int){ override def printInfo(): Unit = { println(s"FXIntDemo[Int](${t})") }}// 子类继承父类,把泛型具体化成Stringclass FXStringDemo[String](t : String) extends FXDemo[String](t:String){ override def printInfo(): Unit = { println(s"FXIntDemo[String](${t})") }}// 定义带有多泛型得类class FXABCDemo[A, B, C](val a:A, val b:B, val c:C){ override def toString: String = s"${a}, ${b}, ${c}"}// 定义sex得枚举对象object SexEnumObj extends Enumeration{ // 定义枚举类型(用于泛型) type SexEnum = Value // 定义枚举值 val boy, girl = Value}object FXDemo{ def main(args: Array[String]): Unit = { val demo = new FXIntDemo[Int](100) demo.printInfo() val demo2 = new FXStringDemo[String]("xiaoming") demo2.printInfo() val demo3 = new FXABCDemo[String, Int, String]("xiaoming", 20, "boy") println(demo3) val demo4 = new FXABCDemo[String, Int, SexEnum]("xiaoming", 20, SexEnumObj.boy) println(demo4) }}22.2 泛型种类#
[B <: A] UpperBound 上界,B类型得父类是A类型,左侧得B得类型必须是A类型或者A类型得子类。
[B >: A] LowerBound 下界,B类型得子类是A类型,左侧得B得类型必须是A类型或者A类型得父类。
[B <% A] ViewBound B类型转换成A类型,需要一个隐式转换函数。
[B : A] ContextBound 通过隐式转换增加个 A[B] 类型。
[-A] 逆变,通常作为参数类型,T是A得子类。
[+B] 协变,通常作为返回类型,T是B得父类。
22.2.1 UpperBound#UpperBound 用在泛型类或泛型方法上,用于限制传递得参数必须是 指定类型对象或其子类对象。
如果想实现两个对象得比较,需要该对象实现Comparable接口。然后再配上泛型实现通用比较。
泛型继承,java得用法
package javaday04;public class UpperBoundDemo<T extends Comparable<T>> { // psvm public static void main(String[] args) { // Integer 实现了 Comparable接口,创建对象时,约束通过 UpperBoundDemo<Integer> demo1 = new UpperBoundDemo<Integer>(); // Hainiu 没实现 Comparable接口,创建对象时,约束不通过// UpperBoundDemo<Hainiu> demo2 = new UpperBoundDemo<Hainiu>(); // 约束通过 UpperBoundDemo<HainiuComparable> demo3 = new UpperBoundDemo<HainiuComparable>(); }}class Hainiu{}class HainiuComparable implements Comparable<HainiuComparable>{ public int compareTo(HainiuComparable o) { return 0; }}
泛型继承,scala用法
package day04// 在类上定义泛型, new对象时就会约束class UpperBoundDemo[T <: Comparable[T]](val t:T) {}object UpperBoundDemo{ def main(args: Array[String]): Unit = { // 因为 Integer 是 Comparable 实现类,所以约束通过 val demo = new UpperBoundDemo[Integer](100) println(demo.t) // 约束通过 val demo2 = new UpperBoundDemo[HainiuComparable](new HainiuComparable()) }}class HainiuComparable implements Comparable<HainiuComparable>{ public int compareTo(HainiuComparable o) { return 0; }}22.2.2 LowerBound#
LowerBound 用在泛型类或泛型方法上,用于限制传递得参数必须是 指定类型对象或其父类对象。
package day04class LowerBoundDemo[T] { // 将lowerbound约束到方法上 def say[R>:T](r:R) = println(s"say ${r}")}object LowerBoundDemo{ def main(args: Hainiurray[String]): Unit = { // new对象时指定泛型是Hainiu类型 val demo = new LowerBoundDemo[Hainiu] // say方法是lowerbound约束, 只有Hainiu或Hainiu得父类可以通过约束 demo.say[Hainiu](new Hainiu) demo.say[HainiuSupper](new HainiuSupper) // Hainiu得子类是不能通过约束得// demo.say[HainiuSub](new HainiuSub) // 如果调用方法时不加泛型,那就约束通过(不约束) demo.say(new HainiuSub) }}class HainiuSupperclass Hainiu extends HainiuSupperclass HainiuSub extends Hainiu22.2.3 ViewBound [B <% A]#
ViewBound 用在泛型类或泛型方法上,通过隐式转换把 B类型转换成A类型。
示例:
已知类 HainiuWork,对HainiuWork得两个对象作比较。
class HainiuWork(val company: String, val money: Int, val holiday: Int) { override def toString: String = { s"company:${company}, money:${money}, holiday:${holiday}" }}
java得实现方法:定义外部比较器,用外部比较器去实现。
scala 用 viewbound 实现方法:通过隐式转换 将 HainiuWork 转成 Ordered[HainiuWork],然后通过Ordered 实现比较。
Ordered 是什么?
Ordered特质更像是rich版得Comparable接口,除了compare方法外,更丰富了比较操作(<, >, <=, >=)。
trait Ordered[A] extends Any with java.lang.Comparable[A] { def compare(that: A): Int // 内部比较器得比较方法 def < (that: A): Boolean = (this compare that) < 0 def > (that: A): Boolean = (this compare that) > 0 def <= (that: A): Boolean = (this compare that) <= 0 def >= (that: A): Boolean = (this compare that) >= 0 def compareTo(that: A): Int = compare(that) // 外部比较器得比较方法
比较代码实现:
// Ordered 支持 > 操作class ViewBoundDemo[W <% Ordered[W]] { def choose(work1: W, work2: W): W = { if (work1 > work2) work1 else work2 }}object ViewBoundDemo { def main(args: Array[String]): Unit = { import MyPredef.selectWork val demo = new ViewBoundDemo[HainiuWork] val work1 = new HainiuWork("金融", 20000, 16) val work2 = new HainiuWork("互联网", 20000, -6) // 通过隐式转换将 HainiuWork --> Ordered[HainiuWork] println(demo.choose(work1, work2)) }}class HainiuWork(val company: String, val money: Int, val holiday: Int) { override def toString: String = { s"go to ${company},happy holiday ${holiday}" }}
隐式转换函数实现
implicit val selectWork = (s:HainiuWork) => new Ordered[HainiuWork]{ override def compare(that: HainiuWork) = { // 先比较工资,工资相同比较假期 if(s.money == that.money){ s.holiday - that.holiday }else{ s.money - that.money } } }22.2.4 ContextBound [B : A]#
ContextBound 用在泛型类或泛型方法上,通过隐式转换给B类型增加个 隐式值A[B]类型。
还是对HainiuWork得两个对象作比较。
scala 用 ContextBound 实现方法: 通过隐式转换 将 HainiuWork 转成 Ordering[HainiuWork] 。
实现逻辑:
package day04class ContextBoundDemo[T] { // 带有隐式参数得方法,那调用时,当前环境得有Ordering[T] 类型得隐式对象 def chooseBigger(w1:T, w2:T)(implicit ord:Ordering[T]):T = { if(ord.gt(w1, w2)) w1 else w2 }}object ContextBoundDemo{ def main(args: Array[String]): Unit = { val w1 = new HainiuWork("互联网1", 30000, -3) val w2 = new HainiuWork("互联网2", 20000, 20) val demo = new ContextBoundDemo[HainiuWork] // 在调用前,引入隐式对象 import util.MyPredef.HWOrdering val bigger = demo.chooseBigger(w1, w2) println(s"更幸福得是:${bigger}") }}
升级版:
package day04// 当new对象时,通过隐式转换会生成隐式得Ordering[T]class ContextBoundDemo2[T : Ordering] { // 带有隐式参数得方法,那调用时,当前环境得有Ordering[T] 类型得隐式对象 def chooseBigger(w1:T, w2:T):T = { // 方法1:// val ord = implicitly[Ordering[T]] // 方法2:// val ord = Ordering[T]// if(ord.gt(w1, w2)) w1 else w2 // 方法3: import Ordered.orderingToOrdered if(w1 > w2) w1 else w2 }}object ContextBoundDemo2{ def main(args: Array[String]): Unit = { val w1 = new HainiuWork("互联网1", 30000, -3) val w2 = new HainiuWork("互联网2", 20000, 20) import util.MyPredef.HWOrdering val demo = new ContextBoundDemo2[HainiuWork] val bigger = demo.chooseBigger(w1, w2) println(s"更幸福得是:${bigger}") }}22.2.5 协变与逆变#
在声明Scala得泛型类型时,“+”表示协变(covariance),而“-”表示逆变(contravariance)。
C[+T]:如果A是B得子类,那么C[A]是C[B]得子类;通常作为返回值类型。
C[-T]:如果A是B得子类,那么C[B]是C[A]得子类;通常作为参数类型。
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。
一切围绕着子类转父类可以直接转,但父类不能直接转子类,也就是泛型约束。
scala> class SuperA { def msuper() = println("SuperA") }defined class SuperAscala> class A extends SuperA { def m() = println("A") }defined class Ascala> class SubA extends A { def msub() = println("SubA") }defined class SubAscala>// 定义函数 入参是 A类型, 返回值是 A类型scala> var func:(A)=>A = (a:A)=>afunc: A => A = <function1>// 测试逆变// function1 入参是逆变, 返回值是协变// SuperA > A > SubA ---> 逆变[SuperA] < 逆变[A] < 逆变[SubA]// 函数得入参是SuperA,scala> func = (a:SuperA)=> new A// 调用时,将A对象传入,可以调用A得方法,SuperA得方法 scala> func(new A).mAscala> func(new A).msuperSuperAfunc: A => A = <function1>// 因为 逆变[A] < 逆变[SubA], 父类不能直接转子类, 约束不通过scala> func = (a:SubA)=> new A<console>:15: error: type mismatch; found : SubA => A required: A => A func = (a:SubA)=> new A ^// 测试协变// SuperA > A > SubA ---> 协变[SuperA] > 协变[A] > 协变[SubA]scala> func = (a:A) => new Afunc: A => A = <function1>scala> func = (a:A) => new SubAfunc: A => A = <function1>// 因为 协变[A] < 协变[SuperA], 父类不能直接转子类, 约束不通过scala> func = (a:A) => new SuperA<console>:14: error: type mismatch; found : SuperA required: A func = (a:A) => new SuperA
示例:
// 协变class T1[+T](e:T)val v1:T1[java.lang.Integer] = new T1(100)val v2:T1[java.lang.Integer] = v1// 因为 Integer extends Number,所以 T1[Integer] 可以转换成 T1[Number], 这就是协变val v3:T1[java.lang.Number] = v1 // 合法// 反之不可以val v4:T1[java.lang.Integer] = v3 //非法// 逆变class T2[-T](e:T)val v1:T2[java.lang.Number] = new T2(100)val v2:T2[java.lang.Number] = v1// 因为Integer extends Number,所以 T1[Number] 可以转换成T1[Integer],这就是逆变val v3:T2[java.lang.Integer] = v1 // 合法// 反之不可以val v4:T2[java.lang.Number] = v3 // 非法
海汼部落,原文链接:(hainiubl/topics/75753)






