本节主要内容

  1. 类型变量界定(Type Variable Bound)
  2. 视图界定(View Bound)
  3. 上界(Upper Bound)与下界(Lower Bound)

1. 类型变量界定(Type Variable Bound)

类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩下泛型的具体范围,例如:
//下面的类编译通不过
//因为泛型T在编译的时候不能确定其具体类型
//即并不是所有的类中都存在compareTo方法
class TypeVariableBound {def compare[T](first:T,second:T)={if (first.compareTo(second)>0) first else second}
}object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B")) }
}

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

class TypeVariableBound {//采用<:进行类型变量界定//该语法的意思是泛型T必须是实现了Comparable//接口的类型def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second}
}object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBound//由于String类型实现了Comparable接口//下面这种使用方式是合法的println(tvb.compare("A", "B"))}
}

从上述代码可以看到,compare方法中如果输入的类型处于Comparable类对应继承层次结构中,则是合法的,否则的话编译会报错,例如:

//定义一个case class类Person
case class Person(var name:String,var age:Int)class TypeVariableBound {def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second}
}object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B"))//下面这一行代码会报错,这是因为Person类并没有//实现Comparable接口println(tvb.compare(Person("stephen",19), Person("john",20)))  }
}

如果需要输入Person类也合法的话,则Person类要实现Comparable接口,代码如下:

//声明Person类为case class且实现了Comparable接口
case class Person(var name:String,var age:Int) extends Comparable[Person]{def compareTo(o:Person):Int={if (this.age>o.age) 1 else if(this.age==o.age) 0else -1}
}class TypeVariableBound {def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second}
}object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B"))//此时下面这条语句是合法的,因为//Person类实现了Comparable接口println(tvb.compare(Person("stephen",19), Person("john",20)))}
}

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

//定义Student类为case class,且泛型T的类型变量界定为AnyVal
//在创建类时,所有处于AnyVal类继承层次结构的类都是合法的
//如Int、Double等值类型
case class Student[S,T <: AnyVal](var name:S,var hight:T)
object TypeVariableBound{def main(args: Array[String]): Unit = {//下面这条语句是不合法的,因为String类型不属于//AnyVal类层次结构// val S1=Student("john","170")//下面这两条语句都是合法的,因为//Int,Long类型都是AnyValval S2=Student("john",170.0)val S3=Student("john",170L)}
}

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

2. 视图界定(View Bound)

上一节将的类型变量界定建立在类继承层次结构的基础上,但有时候这种限定不能满足实际要求,如果希望跨越类继承层次结构时,可以使用视图界定来实现的,其后面的原理是通过隐式转换(我们在下一讲中会详细讲解什么是隐式转换)来实现。视图界定利用<%符号来实现,在上一节中提到:

//使用的是类型变量界定
case class Student[T,S <: Comparable[S]](var name:T,var height:S)
object ViewBound extends App{val s= Student("john","170")//下面这条语句不合法,这是因为//Int类型没有实现Comparable接口val s2= Student("john",170)
}

上面这个问题可以通过视图界定来解决,代码如下:

//利用<%符号对泛型S进行限定
//它的意思是S可以是Comparable类继承层次结构
//中实现了Comparable接口的类
//也可以是能够经过隐式转换得到的类,该类实现了
//Comparable接口
case class Student[T,S <% Comparable[S]](var name:T,var height:S)object ViewBound extends App{val s= Student("john","170")//下面这条语句在视图界定中是合法的//因为Int类型此时会隐工转换为//RichInt类,而RichInt类属于Comparable、//继承层次结构val s2= Student("john",170)
}

查看Scala API文档可以看到

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

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

trait Ordered混入了Comparable接口

可以看到,视图界定比类型变量界定的限制要宽松一点,它不但可以是类继承层次结构中的类,也可以跨越类继承层次结构,这后台的实现方式是通过隐式转换来进行的。

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

下类代码其实是类型参数中经常提到的上界,这是因为它限定了继承层次结构中最顶层的类,例如T <: AnyVal表示泛型T的类型的最顶层类是AnyVal,所有输入是AnyVal的子类都是合法的,其它的都是非法的,因为被称为上界,有点像x<=3这样的数学比较。

case class Student[S,T <: AnyVal](var name:S,var hight:T)
  • 1

除了上界之外,还有个非常重要的内容就是下界,下界通过>:符号来标识,代码如下:

class Pair1[T](val first:T,val second:T){//下界通过[R >: T]的意思是//泛型R的类型必须是T的超类def replaceFirst[R >: T](newFirst:R)= new Pair1[R](newFirst,second)override def toString()=first+"---"+second
}//Book类
class Book(val name:String){override def toString()="name--"+name
}
//Book子类Ebook
class Ebook(name:String) extends Book(name)
//Book子类Pbook
class Pbook(name:String) extends Book(name)
//Pbook子类,WeridBook
class WeirdBook(name:String) extends Pbook(name)object LowerBound extends App{val first = new Ebook("hello")val second = new Pbook("paper book")val p1 = new Pair1(first,second)println(p1)//scala> val p1 = new Pair1(first,second)//p1: Pair1[Book] = name--hello---name--paper book//Ebook,Pbook,最终得到的类是Pair1[Book]val newFirst = new Book("generic pBook")val p2 = p1.replaceFirst(newFirst)//p2: Pair1[Book] = name--generic pBook---name--paper bookprintln(p2)val weirdFirst:WeirdBook= new WeirdBook("generic pBook")val p3 = p1.replaceFirst(weirdFirst)//p3: Pair1[Book] = name--generic pBook---name--paper bookval p4 = new Pair1(second,second)//p4: Pair1[Pbook] = name--paper book---name--paper bookprintln(p4)val thirdBook=new Book("Super Books")val p5=p4.replaceFirst(thirdBook)println(p5)//下面这条语句会报错//type mismatch; found : cn.scala.xtwy.lowerbound.Pair1[cn.scala.xtwy.lowerbound.Pbook] required: cn.scala.xtwy.lowerbound.Pbookval p6:Pbook=p4.replaceFirst(weirdFirst)
}

通过上述代码发现,如果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的下界限定,从而编译出错。从这里我们可以看到,下界的作用主要是保证类型安全

Scala入门到精通——第十七节 类型参数(一)相关推荐

  1. Scala入门到精通——第二十节 类型参数(二)

    本节主要内容 Ordering与Ordered特质 上下文界定(Context Bound) 多重界定 类型约束 1. Ordering与Ordered特质 在介绍上下文界定之前,我们对Scala中的 ...

  2. Scala入门到精通——第二十一节 类型参数(三)-协变与逆变

    本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} .当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S] ...

  3. Scala入门到精通——第二十七节 Scala操纵XML

    本节主要内容 XML 字面量 XML内容提取 XML对象序列化及反序列化 XML文件读取与保存 XML模式匹配 1. XML 字面量 XML是一种非常重要的半结构化数据表示方式,目前大量的应用依赖于X ...

  4. Scala入门到精通——第十一节 Trait进阶

    本节主要内容 trait构造顺序 trait与类的比较 提前定义与懒加载 trait扩展类 self type 1 trait构造顺序 在前一讲当中我们提到,对于不存在具体实现及字段的trait,它最 ...

  5. Scala入门到精通——第十节 Scala类层次结构、Traits初步

    本节主要内容 Scala类层次结构总览 Scala中原生类型的实现方式解析 Nothing.Null类型解析 Traits简介 Traits几种不同使用方式 1 Scala类层次结构 Scala中的类 ...

  6. Scala入门到精通——第七节:类和对象(二)

    本节主要内容 单例对象 伴生对象与伴生类 apply方法 应用程序对象 抽象类 单例对象 在某些应用场景下,我们可能不需要创建对象,而是想直接调用方法,但是Scala语言并不支持静态成员,scala通 ...

  7. Scala入门到精通——第六节:类和对象(一)

    本节主要内容 1 类定义.创建对象 2 主构造器 3 辅助构造器 类定义.创建对象 //采用关键字class定义 class Person {//类成员必须初始化,否则会报错//这里定义的是一个公有成 ...

  8. Scala入门到精通——第五节 函数、高阶函数与闭包

    本节主要内容 (一)函数字面量(值函数) (二)匿名函数 (三)函数的简化 (四)函数参数 (四)闭包 函数字面量(值函数) 函数字面量(function literal),也称值函数(functio ...

  9. Scala入门到精通——第四节 Set、Map、Tuple、队列操作实战

    本节主要内容 mutable.immutable集合 Set操作实战 Map操作实战 Tuple操作实战 队列操作实战 栈操作实战 mutable.immutable集合 以下内容来源于Scala官方 ...

最新文章

  1. codevs 1227 方格取数 2
  2. JZOJ 5305. 【NOIP2017提高A组模拟8.18】C
  3. cdt开发java_Eclipse:可以将CDT和Java IDE放在一起
  4. RT-Thread Nano版本和RT-Thread Master版本的关系
  5. jquery显示和隐藏元素
  6. python怎么发图文_用Python发一封图文并茂的邮件
  7. h5离线缓存与浏览器缓存的区别
  8. 矩阵链乘——动态规划
  9. Two Sum(C、C++、Python)
  10. 15套前端经典实战项目大合集,小白练手必备实战项目
  11. 线性变换的不变子空间和特征子空间的关系
  12. win0如何添加计算机用户,Win10系统如何添加来宾账户 win10添加来宾账户的方法
  13. 自发光材质、panner、sin——移动的光栅
  14. 批量提取文件创建时间_文件列表提取软件的使用
  15. 异地远程群晖NAS教程【cpolar内网穿透】
  16. 14位数仿科学真计算机,十四位数仿真计算器
  17. ADSL/光纤 双拨,多拨
  18. 计算机配置主要看哪些东西,买电脑主要看哪些配置
  19. 初级SQL开发汇总指南
  20. Python列表去重的几种方法和实例

热门文章

  1. 【图示】小程序云开发和不使用云开发的区别
  2. 27行代码AC_迷宫 2017年第八届蓝桥杯A组第一题(暴力、仿迷宫)
  3. 解题报告——习题2-5 分数化小数(decimal) 输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。
  4. Python程序开发——Python实现可增删改查的员工管理系统
  5. linux系统支持uefi,支持UEFI启动的 Puppy Linux 7.5发布,Linux 4.4和4.9 LTS内核
  6. linux终端命令教程,Linux终端命令入坑技巧
  7. 蝴蝶曲线python_ProE常用曲线方程:Python Matplotlib 版本代码(蝴蝶曲线)
  8. python坐标怎么打_python导入坐标点的操作方法
  9. win怎么打开微软更新服务器,怎样打开win10的更新服务器地址
  10. 液晶弹性自由能计算_自由能方法应用(一)开放计算平台BRIDGE的介绍及使用案例...