Scala入门到精通——第十八节 隐式转换与隐式参数(一)
本节主要内容
- 隐式转换简介
- 隐式转换函数
- 隐式转换规则
- 隐式参数
1. 隐式转换简介
在Scala语言当中,隐式转换是一项强大的程序语言功能,它不仅能够简化程序设计,也能够使程序具有很强的灵活性。要想更进一步地掌握scala语言,了解其隐式转换的作用与原理是很有必要的,否则很难得以应手地处理日常开发中的问题。
在scala语言中,隐式转换是无处不在的,只不过scala语言为我们隐藏了相应的细节,例如scala中的类继承层次结构中:
它们存在固有的隐式转换,不需要人工进行干预,例如Float在必要情况下自动转换为Double类型
在前一讲的视图界定中我们也提到,视图界定可以跨越类层次结构进行,它背后的实现原理就是隐式转换,例如Int类型会视图界定中会自动转换成RichInt,而RichInt实现了Comparable接口,当然这里面的隐式转换也是scala语言为我们设计好的
本节将对隐式转换中的隐式转换函数、隐式转换规则、隐式参数进行介绍,使大家明白如何自己实现隐式转换操作。
2. 隐式转换函数
下列赋值如果没有隐式转换的话会报错:
scala> val x:Int=3.5
<console>:7: error: type mismatch;found : Double(3.5)required: Intval x:Int=3.5^
添加隐式转换函数后可以实现Double类型到Int类型的赋值
//定义了一个隐式函数double2Int,将输入的参数
//从Double类型转换到Int类型
scala> implicit def double2Int(x:Double)=x.toInt
warning: there were 1 feature warning(s); re-run with -feature for details
double2Int: (x: Double)Int
//定义完隐式转换后,便可以直接将Double类型赋值给Int类型
scala> val x:Int=3.5
x: Int = 3
- 1
隐式函数的名称对结构没有影响,即implicit def double2Int(x:Double)=x.toInt函数可以是任何名字,只不能采用source2Target这种方式函数的意思比较明确,阅读代码的人可以见名知义,增加代码的可读性。
隐式转换功能十分强大,可以快速地扩展现有类库的功能,例如下面的代码:
package cn.scala.xtwyimport java.io.File
import scala.io.Source
//RichFile类中定义了Read方法
class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString
}object ImplicitFunction extends App{implicit def double2Int(x:Double)=x.toIntvar x:Int=3.5//隐式函数将java.io.File隐式转换为RichFile类implicit def file2RichFile(file:File)=new RichFile(file)val f=new File("file.log").readprintln(f)
}
3. 隐式转换规则
隐式转换可以定义在目标文件当中,例如
implicit def double2Int(x:Double)=x.toInt
var x:Int=3.5
隐式转换函数与目标代码在同一个文件当中,也可以将隐式转换集中放置在某个包中,在使用进直接将该包引入即可,例如:
package cn.scala.xtwy
import java.io.File
import scala.io.Source//在cn.scala.xtwy包中定义了子包implicitConversion
//然后在object ImplicitConversion中定义所有的引式转换方法
package implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toIntimplicit def file2RichFile(file:File)=new RichFile(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString
}object ImplicitFunction extends App{//在使用时引入所有的隐式方法import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5val f=new File("file.log").readprintln(f)
}
这种方式在scala语言中比较常见,在前面我们也提到,scala会默认帮我们引用Predef对象中所有的方法,Predef中定义了很多隐式转换函数,下面是Predef的部分隐式转换源码:
scala> :implicits -v
/* 78 implicit members imported from scala.Predef *//* 48 inherited from scala.Predef */implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A]implicit def any2Ensuring[A](x: A): Ensuring[A]implicit def any2stringadd(x: Any): runtime.StringAddimplicit def any2stringfmt(x: Any): runtime.StringFormatimplicit def boolean2BooleanConflict(x: Boolean): Objectimplicit def byte2ByteConflict(x: Byte): Objectimplicit def char2CharacterConflict(x: Char): Objectimplicit def double2DoubleConflict(x: Double): Objectimplicit def float2FloatConflict(x: Float): Objectimplicit def int2IntegerConflict(x: Int): Objectimplicit def long2LongConflict(x: Long): Objectimplicit def short2ShortConflict(x: Short): Object//....................
那什么时候会发生隐式转换呢?主要有以下几种情况:
1 当方法中参数的类型与实际类型不一致时,例如
def f(x:Int)=x
//方法中输入的参数类型与实际类型不一致,此时会发生隐式转换
//double类型会转换为Int类型,再进行方法的执行
f(3.14)
2 当调用类中不存在的方法或成员时,会自动将对象进行隐式转换,例如:
package cn.scala.xtwyimport java.io.File
import scala.io.Source
//RichFile类中定义了Read方法
class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString
}object ImplicitFunction extends App{implicit def double2Int(x:Double)=x.toIntvar x:Int=3.5//隐式函数将java.io.File隐式转换为RichFile类implicit def file2RichFile(file:File)=new RichFile(file)//File类的对象并不存在read方法,此时便会发生隐式转换//将File类转换成RichFileval f=new File("file.log").readprintln(f)
}
- 1
前面我们讲了什么情况下会发生隐式转换,下面我们讲一下什么时候不会发生隐式转换:
1 编译器可以不在隐式转换的编译通过,则不进行隐式转换,例如
//这里定义了隐式转换函数
scala> implicit def double2Int(x:Double)=x.toInt
warning: there were 1 feature warning(s); re-run with -feature for details
double2Int: (x: Double)Int//下面几条语句,不需要自己定义隐式转换编译就可以通过
//因此它不会发生前面定义的隐式转换
scala> 3.0*2
res0: Double = 6.0scala> 2*3.0
res1: Double = 6.0scala> 2*3.7
res2: Double = 7.4
2 如果转换存在二义性,则不会发生隐式转换,例如
package implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toInt//这里定义了一个隐式转换implicit def file2RichFile(file:File)=new RichFile(file)//这里又定义了一个隐式转换,目的与前面那个相同implicit def file2RichFile2(file:File)=new RichFile(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString
}object ImplicitFunction extends App{import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5//下面这条语句在编译时会出错,提示信息如下://type mismatch; found : java.io.File required:// ?{def read: ?} Note that implicit conversions //are not applicable because they are ambiguous: //both method file2RichFile in object //ImplicitConversion of type (file: //java.io.File)cn.scala.xtwy.RichFile and method //file2RichFile2 in object ImplicitConversion of //type (file: java.io.File)cn.scala.xtwy.RichFile //are possible conversion functions from java.io.File to ?{def read: ?}
value read is not a member of java.io.Fileval f=new File("file.log").readprintln(f)
}
编译提示隐式转换存在二义性(ambiguous)
3 隐式转换不会嵌套进行,例如
package cn.scala.xtwy
import java.io.File
import scala.io.Sourcepackage implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toIntimplicit def file2RichFile(file:File)=new RichFile(file)//implicit def file2RichFile2(file:File)=new RichFile(file)implicit def richFile2RichFileAnother(file:RichFile)=new RichFileAnother(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString
}//RichFileAnother类,里面定义了read2方法
class RichFileAnother(val file:RichFile){def read2=file.read
}object ImplicitFunction extends App{import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5//隐式转换不会多次进行,下面的语句会报错//不能期望会发生File到RichFile,然后RifchFile到//RichFileAnthoer的转换val f=new File("file.log").read2println(f)
}
理解了这些规则之后,在使用隐式转换时才能够得心应手
4. 隐式参数
在一般的函数据定义过程中,需要明确传入函数的参数,代码如下:
package cn.scala.xtwyclass Student(var name:String){//将Student类的信息格式化打印def formatStudent(outputFormat:OutputFormat)={outputFormat.first+" "+this.name+" "+outputFormat.second}
}class OutputFormat(var first:String,val second:String)object ImplicitParameter {def main(args: Array[String]): Unit = {val outputFormat=new OutputFormat("<<",">>")println(new Student("john").formatStudent(outputFormat))}
}
//执行结果
//<< john >>
如果给函数定义隐式参数的话,则在使用时可以不带参数,代码如下:
package cn.scala.xtwy
class Student(var name:String){//利用柯里化函数的定义方式,将函数的参数利用//implicit关键字标识//这样的话,在使用的时候可以不给出implicit对应的参数def formatStudent()(implicit outputFormat:OutputFormat)={outputFormat.first+" "+this.name+" "+outputFormat.second}
}class OutputFormat(var first:String,val second:String)object ImplicitParameter {def main(args: Array[String]): Unit = {//程序中定义的变量outputFormat被称隐式值implicit val outputFormat=new OutputFormat("<<",">>")//在.formatStudent()方法时,编译器会查找类型//为OutputFormat的隐式值,本程序中定义的隐式值//为outputFormatprintln(new Student("john").formatStudent())}
}
Scala入门到精通——第十八节 隐式转换与隐式参数(一)相关推荐
- Scala入门到精通——第二十八节 Scala与JAVA互操作
本节主要内容 JAVA中调用Scala类 Scala中调用JAVA类 Scala类型参数与JAVA泛型互操作 Scala与Java间的异常处理互操作 1. JAVA中调用Scala类 Java可以直接 ...
- Scala入门到精通——第十九节 隐式转换与隐式参数(二)
本节主要内容 隐式参数中的隐式转换 函数中隐式参数使用概要 隐式转换问题梳理 1. 隐式参数中的隐式转换 前一讲中,我们提到函数中如果存在隐式参数,在使用该函数的时候如果不给定对应的参数,则编译器会自 ...
- Scala入门到精通——第二十节 类型参数(二)
本节主要内容 Ordering与Ordered特质 上下文界定(Context Bound) 多重界定 类型约束 1. Ordering与Ordered特质 在介绍上下文界定之前,我们对Scala中的 ...
- Scala入门到精通——第十三节 高阶函数
本节主要内容 高阶函数简介 Scala中的常用高阶函数 SAM转换 函数柯里化 部分应用函数 1. 高阶函数简介 高阶函数主要有两种:一种是将一个函数当做另外一个函数的参数(即函数参数):另外一种是返 ...
- Scala入门到精通——第二十九节 Scala数据库编程
本节主要内容 Scala Mavenproject的创建 Scala JDBC方式訪问MySQL Slick简单介绍 Slick数据库编程实战 SQL与Slick相互转换 本课程在多数内容是在官方教程 ...
- Scala入门到精通——第二十四节 高级类型 (三)
本节主要内容 Type Specialization Manifest.TypeTag.ClassTag Scala类型系统总结 在Scala中,类(class)与类型(type)是两个不一样的概念. ...
- Scala入门到精通——第十四节 Case Class与模式匹配(一)
本节主要内容 模式匹配入门 Case Class简介 Case Class进阶 1. 模式匹配入门 在Java语言中存在switch语句,例如: //下面的代码演示了java中switch语句的使用 ...
- Scala入门到精通——第二十六节 Scala并发编程基础
本节主要内容 Scala并发编程简介 Scala Actor并发编程模型 react模型 Actor的几种状态 Actor深入使用解析 1. Scala并发编程简介 2003 年,Herb Sutte ...
- Scala入门到精通——第二十五节 提取器(Extractor)
本节主要内容 apply与unapply方法 零变量或变量的模式匹配 提取器与序列模式 scala中的占位符使用总结 1. apply与unapply方法 apply方法我们已经非常熟悉了,它帮助我们 ...
最新文章
- php学习笔记之static的问题
- bat等大公司常考java多线程面试题
- YBTOJ洛谷P2839:最大中位数(主席树、二分答案)
- linux使用vim开启文档,linux 配置 直接用VIM默认打开文件
- C++笔记-ClassA a和ClassA a()的区别与联系
- SpringMVC注解驱动开发
- qpsk psk matlab,BPSK和QPSK调制解调原理及MATLAB程序资料
- 算法在计算机中的作用
- 高性能科学计算、工程计算仿真用电脑装机经验
- ResNet再进化!重新思考ResNet:采用高阶方案的改进堆叠策略
- 微信小程序--首页加载界面demo
- 我的创作纪念日的温柔与七夕的浪漫交织了在一起
- html+css实现京东、英雄联盟静态页面
- bugku 我永远喜欢穹妹
- svn服务器端下载linux,Svn linux服务端安装及配置
- 各种排序算法的总结和比较
- mysql 配置定时任务_Mysql定时任务
- 2021年危险化学品经营单位主要负责人新版试题及危险化学品经营单位主要负责人证考试
- Excel文档总表与分表单(sheet)如何批量设置超链接跳转
- Substance Painter 学习笔记 (一)
热门文章
- 【答案放在最后,看题看不到答案】2017年下半年软件设计师 上午选择题
- 你不知道LinkedList中的方法
- Python程序开发——第四章 字典与集合
- 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)
- shell之case和循环语句(case语句的格式与举例)(for循环,while循环until循环语句的详解和continue,break解释, 九九乘法口诀表 ,等腰三角形)
- php dedecms 记录访问者ip,dedecms实现显示访问者ip地址的办法
- php 做fft,什么是numpy.fft.rfft和numpy.fft.irfft及其在MATLA...
- assert()函数_【工程师分享】避免Xil_Assert系列宏导致的死循环
- 井下三专两闭锁的内容_局部通风机三专两闭锁具体规定
- 好想学python 怎么猜人物_想自学Python,如何才能坚持下来?