美文网首页
scala 系列之 18scala 泛型

scala 系列之 18scala 泛型

作者: 海牛大数据_青牛老师 | 来源:发表于2021-08-29 13:41 被阅读0次

22 泛型

泛型就是不确定的类型,可以在类或方法不确实传入类型时使用,可以提高代码的灵活性和复用性;

scala中泛型的用法和java中差不多,但是会有一些自己独特的语法;

泛型类:指定类可以接受任意类型参数。

泛型方法:指定方法可以接受任意类型参数。

22.1 泛型类基本用法

package day04
import day04.SexEnumObj.SexEnum
// 定义带有泛型的抽象类
abstract class FXDemo[T](val t : T) {
  def printInfo():Unit
}
// 子类继承父类,把泛型具体化成Int
class FXIntDemo[Int](t : Int) extends FXDemo[Int](t:Int){
  override def printInfo(): Unit = {
    println(s"FXIntDemo[Int](${t})")
  }
}
// 子类继承父类,把泛型具体化成String
class 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 day04
class 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 HainiuSupper
class Hainiu extends HainiuSupper
class HainiuSub extends Hainiu

22.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] 。

file

实现逻辑:

package day04
class 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]没有从属关系。

file

一切围绕着子类转父类可以直接转,但父类不能直接转子类,也就是泛型约束。

scala> class SuperA { def msuper() = println("SuperA") }
defined class SuperA
scala> class A extends SuperA { def m() = println("A") }
defined class A
scala> class SubA extends A { def msub() = println("SubA") }
defined class SubA
scala>
// 定义函数 入参是 A类型, 返回值是 A类型
scala> var func:(A)=>A = (a:A)=>a
func: A => A = <function1>
// 测试逆变
// function1 入参是逆变, 返回值是协变
// SuperA > A > SubA   ---> 逆变[SuperA] < 逆变[A] < 逆变[SubA]
// 函数的入参是SuperA,
scala> func = (a:SuperA)=> new A
// 调用时,将A对象传入,可以调用A的方法,SuperA的方法 
scala> func(new A).m
A
scala> func(new A).msuper
SuperA
func: 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 A
func: A => A = <function1>
scala> func = (a:A) => new SubA
func: 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 // 非法
     

海汼部落原创文章,原文链接:http://hainiubl.com/topics/75753

相关文章

  • 好程序员大数据培训分享Scala系列之泛型

    好程序员大数据培训分享Scala系列之泛型,带有一个或多个类型参数的类是泛型的。 泛型类的定义: //带有类型参数...

  • Scala泛型

    泛型类是以类型作为参数,Scala类型参数放在方括号[]中,Java放在<>中 变型 Variance Scala...

  • Scala泛型

    泛型的意思是 泛指某种具体的数据类型 , 在Scala中, 泛型用 [数据类型] 表示. 在实际开发中, 泛...

  • Scala 泛型以及泛型约束

    泛型类 在类声明时,定义一些泛型类型,然后在类的内部,就可以使用这些泛型类型 在需要对类中的某些成员,如字段或方法...

  • Scala 类型系统

    1.在scala泛型中获取其 Class[T] 需求:获取一个泛型 T 的 class 类型的 Class[T],...

  • scala 泛型类型

    使用泛型类,通常是需要对类中的某些成员,比如某些field和method中的参数或变量,进行统一的类型限制,这样可...

  • Java系列之泛型

    自从 JDK 1.5 提供了泛型概念,泛型使得开发者可以定义较为安全的类型,不至于强制类型转化时出现类型转化异常,...

  • Geekband-third week of part3

    1.泛型算法之变易算法 2.泛型算法之排序 3.泛型算法之泛型数值算法 4.内存分配器

  • [译]Scala泛型类

    泛型类是以一个类型作为参数的类。对于集合类特别有用。 定义泛型类 泛型类以一个类型作为参数,包含在[]中。试用字母...

  • Scala 泛型协变与泛型边界

    代码准备 泛型协变 泛型协变、逆变、不变是指拥有泛型的类在声明和赋值时的对应关系。 协变:声明时泛型是父类,赋值时...

网友评论

      本文标题:scala 系列之 18scala 泛型

      本文链接:https://www.haomeiwen.com/subject/adefpltx.html