Scala类型参数(一)

心已赠人 2022-06-07 01:47 277阅读 0赞

Scala类型参数(一)

类型参数是对泛型的范围进一步的界定,那么介绍类型参数之前先聊聊泛型。Scala类型参数。类型参数是对泛型的范围进一步的界定,那么介绍类型参数之前先聊聊泛型。泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才 被确定,泛型可以有效的增强程序的使用行,使用泛型可以使得类或者方法具有更强的通用性。泛型的典型应用场景是集合及集合中的方法参数,可以说同java一样,scala中泛型无处不在。

类型变量的界定

类型参数是对泛型的范围进一步的界定,对泛型的范围进一步的界定,从而缩小泛型的具体范围,例如:

  1. // 下面的类编译通不过
  2. // 因为泛型T在编译时不能确定其具体类型
  3. // 并不是所有的类都存在compareTo方法
  4. class TypeVariableBound{
  5. def compare[T](first:T, second:T) = {
  6. if(first.compareTo(second) > 0) first else second
  7. }
  8. }
  9. object TypeVariableBound{
  10. def main(args:Array[String]) : Unit = {
  11. val tvb = new TypeVariableBound
  12. println(tvb.compare("A","B"))
  13. }
  14. }

如果在使TypeVariableBound类编译通过,此时可以利用类型变量界定对泛型T进行界定,指明所有的泛型T都实现了Comparable接口,代码如下:

  1. // 下面的类编译通不过
  2. // 因为泛型T在编译时不能确定其具体类型
  3. // 并不是所有的类都存在compareTo方法
  4. class TypeVariableBound{
  5. def compare[T <: Comparable[T]](first:T, second:T) = {
  6. if(first.compareTo(second) > 0) first else second
  7. }
  8. }
  9. object TypeVariableBound{
  10. def main(args:Array[String]) : Unit = {
  11. val tvb = new TypeVariableBound
  12. println(tvb.compare("A","B"))
  13. }
  14. }

上面的类型变量界定都是作用于方法compare上,类型变量界定除了作用于方法上外,还可以对类中泛型进行范围限定,例如:

  1. // 定义Student类为case class,且泛型T的类型变量界定为AnyVal
  2. // 在创建类时,所有处于AnyVal类继承层次结构的类都是合法的
  3. // 如Int、Double等值类型
  4. case class Student[S,T <: AnyVal](name:S, height:T)
  5. object TypeVariableBound{
  6. def main(args:Array[String]):Unit = {
  7. // 下面这条语句是非法的,因为String类型不属于
  8. // AnyVal类层次结构
  9. // val s1 = Student("john","170")
  10. // 下面这两条语句都是合法的,因为
  11. // Int,Long类型都是AnyVal
  12. val s2 = Student("jhon",178)
  13. val s3 = Student("jhon",178L)
  14. }
  15. }

从上面的例子中不难看出,类型变量界定可以对方法和类中的泛型进行范围界定,这种界定建立在类继承层次结构的基础上,通过<:符号将泛型的范围进一步减少。

2 视图界定(View Bound)

上一节将类型变量界定建立在类层次结构的基础上,但有时候这种限定不能满足实际需求,如果希望跨越类继承层次结构时,可以使用视图界定来实现,其后面的原理是通过隐式转换来实现的。视图界定利用<%符号来实现。

  1. // 利用<%符号对泛型S进行限定
  2. // 它的意思可以是Comparable类继承层级结构
  3. // 中实现了Comparable接口的类
  4. // 也可以是能够经过隐式转换得到的类,该类实现了
  5. // Comparable接口
  6. case class Student[T,S <% Comparable[S]](name:T,height:S)
  7. object ViewBound extends App{
  8. val s = Student("john","175")
  9. // 下面这条语句在视图界定中是合法的
  10. // 因为Int类型此时会隐式转换为RichInt类
  11. // RichInt类实现了Comparable特质
  12. val s = Student("john",175)
  13. }

查看Scala API文档可以看到

scala_typevariable_1.png

Int类会隐式转换为RichInt类,RichInt并不是直接实现Comparable接口,而是通过ScalaNumberProxy类将Comparable中的方法继承过来:

scala_typevariable_2.png

ScalaNumberProxy混入了OrderedProxy,而OrderedProxy又混入了Ordered

scala_typevariable_3.png

trait Ordered混入了Comparable接口

可以看到,视图界定比类型变量界定的限制要宽松一点,它不但可以是类继承中的类,也可以是通过隐式转换进行的。

3 上界(Upper Bound)下界(Lower Bound)

例如T <: AnyVal表示泛型T的类型的最顶层类是AnyVal,所有输入是AnyVal的子类都是合法的,其它的都是非法的,因为被称为上界,下界通过符号>:表示。

  1. class Pair1[T](val first:T,val second:T){
  2. //下界通过[R >: T]的意思是
  3. //泛型R的类型必须是T的超类
  4. def replaceFirst[R >: T](newFirst:R)= new Pair1[R](newFirst,second)
  5. override def toString()=first+"---"+second
  6. }
  7. //Book类
  8. class Book(val name:String){
  9. override def toString()="name--"+name
  10. }
  11. //Book子类Ebook
  12. class Ebook(name:String) extends Book(name)
  13. //Book子类Pbook
  14. class Pbook(name:String) extends Book(name)
  15. //Pbook子类,WeridBook
  16. class WeirdBook(name:String) extends Pbook(name)
  17. object LowerBound extends App{
  18. val first = new Ebook("hello")
  19. val second = new Pbook("paper book")
  20. val p1 = new Pair1(first,second)
  21. println(p1)
  22. //scala> val p1 = new Pair1(first,second)
  23. //p1: Pair1[Book] = name--hello---name--paper book
  24. //Ebook,Pbook,最终得到的类是Pair1[Book]
  25. val newFirst = new Book("generic pBook")
  26. val p2 = p1.replaceFirst(newFirst)
  27. //p2: Pair1[Book] = name--generic pBook---name--paper book
  28. println(p2)
  29. val weirdFirst:WeirdBook= new WeirdBook("generic pBook")
  30. val p3 = p1.replaceFirst(weirdFirst)
  31. //p3: Pair1[Book] = name--generic pBook---name--paper book
  32. val p4 = new Pair1(second,second)
  33. //p4: Pair1[Pbook] = name--paper book---name--paper book
  34. println(p4)
  35. val thirdBook=new Book("Super Books")
  36. val p5=p4.replaceFirst(thirdBook)
  37. println(p5)
  38. //下面这条语句会报错
  39. //type mismatch; found : cn.scala.xtwy.lowerbound.Pair1[cn.scala.xtwy.lowerbound.Pbook] required: cn.scala.xtwy.lowerbound.Pbook
  40. val p6:Pbook=p4.replaceFirst(weirdFirst)
  41. }

通过上述代码发现,如果newFirst的类型刚好是T的基类,R就直接是newFirst的类型。如果newFirst的类型不是T的基类,那R就会是T和newFirst的类型的共同基类。当限定返回变量类型时,例如val p6:Pbook=p4.replaceFirst(weirdFirst),由于p4为Pair1[Pbook],也即T为Pbook类型,而replaceFirst(weirdFirst)中的weirdFirst为Pbook的子类,违反了R>:T的下界限定,从而编译出错。从这里我们可以看到,下界的作用主要是保证类型安全。

参考博文

https://yq.aliyun.com/articles/60378?spm=5176.8251999.569296.17.3d10f3b6mLAnf

发表评论

表情:
评论列表 (有 0 条评论,277人围观)

还没有评论,来说两句吧...

相关阅读

    相关 13.Scala-类型参数

    第13章 类型参数 13.1 泛型类 类和特质都可以带类型参数,用方括号来定义类型参数,可以用类型参 数来定义变量、方法参数和返回值。带有一个或多个类型参数的...

    相关 scala array类型参数

    在Scala中,数组(Array)是一种用于存储相同类型元素的数据结构。数组可以用于保存基本数据类型和自定义数据类型的元素。当定义数组类型参数时,您通常是在函数、类或方法签名中

    相关 Scala类型参数()

    Scala类型参数(一) 类型参数是对泛型的范围进一步的界定,那么介绍类型参数之前先聊聊泛型。Scala类型参数。类型参数是对泛型的范围进一步的界定,那么介绍类型参数之前

    相关 Scala类型参数

      类型参数是什么?  类型参数其实就类似于Java中的泛型。也是定义一种类型参数,比如在集合,在类,在函数中, 定义类型参数,然后就可以保证使用到该类型参数的地方,就

    相关 Scala中的类型参数

    类型参数是什么?类型参数其实就类似于Java中的泛型。先说说Java中的泛型是什么,比如我们有List a = new ArrayList(),接着a.add(1),没问题,a