Scala入门到精通——第十七节 类型参数(一)
本节主要内容
- 类型变量界定(Type Variable Bound)
- 视图界定(View Bound)
- 上界(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入门到精通——第十七节 类型参数(一)相关推荐
- Scala入门到精通——第二十节 类型参数(二)
本节主要内容 Ordering与Ordered特质 上下文界定(Context Bound) 多重界定 类型约束 1. Ordering与Ordered特质 在介绍上下文界定之前,我们对Scala中的 ...
- Scala入门到精通——第二十一节 类型参数(三)-协变与逆变
本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} .当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S] ...
- Scala入门到精通——第二十七节 Scala操纵XML
本节主要内容 XML 字面量 XML内容提取 XML对象序列化及反序列化 XML文件读取与保存 XML模式匹配 1. XML 字面量 XML是一种非常重要的半结构化数据表示方式,目前大量的应用依赖于X ...
- Scala入门到精通——第十一节 Trait进阶
本节主要内容 trait构造顺序 trait与类的比较 提前定义与懒加载 trait扩展类 self type 1 trait构造顺序 在前一讲当中我们提到,对于不存在具体实现及字段的trait,它最 ...
- Scala入门到精通——第十节 Scala类层次结构、Traits初步
本节主要内容 Scala类层次结构总览 Scala中原生类型的实现方式解析 Nothing.Null类型解析 Traits简介 Traits几种不同使用方式 1 Scala类层次结构 Scala中的类 ...
- Scala入门到精通——第七节:类和对象(二)
本节主要内容 单例对象 伴生对象与伴生类 apply方法 应用程序对象 抽象类 单例对象 在某些应用场景下,我们可能不需要创建对象,而是想直接调用方法,但是Scala语言并不支持静态成员,scala通 ...
- Scala入门到精通——第六节:类和对象(一)
本节主要内容 1 类定义.创建对象 2 主构造器 3 辅助构造器 类定义.创建对象 //采用关键字class定义 class Person {//类成员必须初始化,否则会报错//这里定义的是一个公有成 ...
- Scala入门到精通——第五节 函数、高阶函数与闭包
本节主要内容 (一)函数字面量(值函数) (二)匿名函数 (三)函数的简化 (四)函数参数 (四)闭包 函数字面量(值函数) 函数字面量(function literal),也称值函数(functio ...
- Scala入门到精通——第四节 Set、Map、Tuple、队列操作实战
本节主要内容 mutable.immutable集合 Set操作实战 Map操作实战 Tuple操作实战 队列操作实战 栈操作实战 mutable.immutable集合 以下内容来源于Scala官方 ...
最新文章
- codevs 1227 方格取数 2
- JZOJ 5305. 【NOIP2017提高A组模拟8.18】C
- cdt开发java_Eclipse:可以将CDT和Java IDE放在一起
- RT-Thread Nano版本和RT-Thread Master版本的关系
- jquery显示和隐藏元素
- python怎么发图文_用Python发一封图文并茂的邮件
- h5离线缓存与浏览器缓存的区别
- 矩阵链乘——动态规划
- Two Sum(C、C++、Python)
- 15套前端经典实战项目大合集,小白练手必备实战项目
- 线性变换的不变子空间和特征子空间的关系
- win0如何添加计算机用户,Win10系统如何添加来宾账户 win10添加来宾账户的方法
- 自发光材质、panner、sin——移动的光栅
- 批量提取文件创建时间_文件列表提取软件的使用
- 异地远程群晖NAS教程【cpolar内网穿透】
- 14位数仿科学真计算机,十四位数仿真计算器
- ADSL/光纤 双拨,多拨
- 计算机配置主要看哪些东西,买电脑主要看哪些配置
- 初级SQL开发汇总指南
- Python列表去重的几种方法和实例
热门文章
- 【图示】小程序云开发和不使用云开发的区别
- 27行代码AC_迷宫 2017年第八届蓝桥杯A组第一题(暴力、仿迷宫)
- 解题报告——习题2-5 分数化小数(decimal) 输入正整数a,b,c,输出a/b的小数形式,精确到小数点后c位。
- Python程序开发——Python实现可增删改查的员工管理系统
- linux系统支持uefi,支持UEFI启动的 Puppy Linux 7.5发布,Linux 4.4和4.9 LTS内核
- linux终端命令教程,Linux终端命令入坑技巧
- 蝴蝶曲线python_ProE常用曲线方程:Python Matplotlib 版本代码(蝴蝶曲线)
- python坐标怎么打_python导入坐标点的操作方法
- win怎么打开微软更新服务器,怎样打开win10的更新服务器地址
- 液晶弹性自由能计算_自由能方法应用(一)开放计算平台BRIDGE的介绍及使用案例...