scala使用implicit实现扩展已有功能
来源:互联网 发布:穿越火线开挂软件 编辑:程序博客网 时间:2024/06/05 20:05
1. 扩展类的功能
假设该类是第三方jar包中的类
class A (val data:Int){ ...}
implicit class B(a:A) { def show { println(a.data) }}
val a = new A(2)a.show()
2. 函数隐式参数
def sorted[B >: A](implicit ord : scala.math.Ordering[B]) : Repr = { /* compiled code */ }
implict val ord = new scala.math.Ordering[B]{ def compare(x : B, y : B) :Int = { /* compiled code */ }}
我们来看一个例子:
def display(input:String):Unit = println(input)
我们可以看到,display
函数的定义只是接受String
类型的入参,因此调用display("any string")
这样的函数是没问题的。但是如果调用display(1)
这样的入参不是String类型的话,编译会出错的。
如果我们想让display函数也能够支持Int类型的入参的话,除了我们重新定一个def display(input:Int):Unit = println(input)
这样的函数以外,我们还可以在相同的作用域内用implicit
关键字定义一个隐式转换函数,示例代码如下:
object ImplicitDemo { def display(input:String):Unit = println(input) implicit def typeConvertor(input:Int):String = input.toString implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"// implicit def booleanTypeConvertor(input:Boolean):String = if(input) "true" else "false" def main(args: Array[String]): Unit = { display("1212") display(12) display(true) }}
我们定义了2个隐式转换函数:
implicit def typeConvertor(input:Int):String = input.toString implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"
这样display
函数就可以接受String、Int、Boolean类型的入参了。注意到上面我们的例子中注释的那一行,如果去掉注释的那一行的话,会在运行的时候出现二义性:
Error:(18, 13) type mismatch; found : Boolean(true) required: StringNote that implicit conversions are not applicable because they are ambiguous: both method typeConvertor in object ImplicitDemo of type (input: Boolean)String and method booleanTypeConvertor in object ImplicitDemo of type (input: Boolean)String are possible conversion functions from Boolean(true) to String display(true) ^
得出的结论是:
隐式转换函数是指在同一个作用域下面,一个给定输入类型并自动转换为指定返回类型的函数,这个函数和函数名字无关,和入参名字无关,只和入参类型以及返回类型有关。注意是同一个作用域。
implicit的应用
我们可以随便的打开scala函数的一些内置定义,比如我们最常用的map函数中->符号,看起来很像php等语言。
但实际上->确实是一个ArrowAssoc类的方法,它位于scala源码中的Predef.scala中。下面是这个类的定义:
final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal { // `__leftOfArrow` must be a public val to allow inlining. The val // used to be called `x`, but now goes by `__leftOfArrow`, as that // reduces the chances of a user's writing `foo.__leftOfArrow` and // being confused why they get an ambiguous implicit conversion // error. (`foo.x` used to produce this error since both // any2Ensuring and any2ArrowAssoc pimped an `x` onto everything) @deprecated("Use `__leftOfArrow` instead", "2.10.0") def x = __leftOfArrow @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y) def →[B](y: B): Tuple2[A, B] = ->(y) } @inline implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
我们看到def ->[B] (y :B)
返回的其实是一个Tuple2[A,B]
类型。
我们定义一个Map:
scala> val mp = Map(1->"game1",2->"game_2")mp: scala.collection.immutable.Map[Int,String] = Map(1 -> game1, 2 -> game_2)
这里 1->"game1"
其实是1.->("game_1")
的简写。
这里怎么能让整数类型1能有->方法呢。
这里其实any2ArrowAssoc
隐式函数起作用了,这里接受的参数[A]是泛型的,所以int也不例外。
调用的是:将整型的1 implicit转换为 ArrowAssoc(1)
看下构造方法,将1当作__leftOfArrow
传入。
->方法的真正实现是生产一个Tuple2类型的对象(__leftOfArrow,y )
等价于(1, "game_id")
这就是一个典型的隐式转换应用。
其它还有很多类似的隐式转换,都在Predef.scala中:
例如:Int,Long,Double都是AnyVal的子类,这三个类型之间没有继承的关系,不能直接相互转换。
在Java里,我们声明Long的时候要在末尾加上一个L,来声明它是long。
但在scala里,我们不需要考虑那么多,只需要:
scala> val l:Long = 10l: Long = 10
这就是implicit函数做到的,这也是scala类型推断的一部分,灵活,简洁。
其实这里调用是:
val l : Long = int2long(10)
更牛逼的功能
为现有的类库增加功能的一种方式,用java的话,只能用工具类或者继承的方式来实现,而在scala则还可以采用隐式转化的方式来实现。
隐式参数
看一个例子再说:
object ImplictDemo { object Context{ implicit val ccc:String = "implicit" } object Param{ def print(content:String)(implicit prefix:String){ println(prefix+":"+content) } } def main(args: Array[String]) { Param.print("jack")("hello") import Context._ Param.print("jack") }}
程序运行结果为:
hello:jackimplicit:jack
隐式转换扩展
import java.io.Fileimport scala.io.Sourceclass RichFile(val file:File){ def read = Source.fromFile(file.getPath()).mkString}object Context{ implicit def file2RichFile(f:File)= new RichFile(f)}object ImplictDemo { def main(args: Array[String]) { import Context.file2RichFile println(new File("f:\\create.sql").read) }}
上面的代码中调用的read方法其实就是RichFile中定义的read方法。
最后的总结:
- 记住隐式转换函数的同一个scop中不能存在参数和返回值完全相同的2个implicit函数。
- 隐式转换函数只在意 输入类型,返回类型。
- 隐式转换是scala的语法灵活和简洁的重要组成部分
- scala使用implicit实现扩展已有功能
- scala implicit使用
- scala implicit class使用
- Scala implicit
- Scala implicit
- Scala implicit
- Scala implicit
- scala implicit
- Scala implicit
- scala implicit
- scala中用implicit实现依赖注入
- scala中用implicit实现依赖注入
- scala 学习总结(一): implicit 函数的使用
- Implicit in Scala
- scala implicit 学习简记
- scala implicit class
- scala implicit 应用相关
- Scala的implicit
- Java Runtime.exec()的使用
- spring+springmvc+mybatis 中 对静态资源的拦截
- TCPdump抓包命令详解
- [Leetcode] 399. Evaluate Division 解题报告
- flex布局的兼容性写法
- scala使用implicit实现扩展已有功能
- Logstash日志插件开发总结(1)
- 第三课 Python爬虫Beautifulsoup4模块的使用
- hdu 3790 最短路径问题 dijkstra算法
- docker容器测试
- 网络图片的爬取和存储
- springMVC的注解详解
- mysql查询手机号码时隐藏其中间四位
- Test_Java_网络编程(模拟客户端向服务器上传文件)