Scala implicit

implicit基本含义

在Scala中有一个关键字是implicit, 之前一直不知道这个货是干什么的,今天整理了一下。

我们先来看一个例子:

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: String
Note 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 = 10
l: 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:jack
implicit:jack

隐式转换扩展

import java.io.File

import scala.io.Source

class 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方法。

最后的总结:

  1. 记住隐式转换函数的同一个scop中不能存在参数和返回值完全相同的2个implicit函数。
  2. 隐式转换函数只在意 输入类型,返回类型。
  3. 隐式转换是scala的语法灵活和简洁的重要组成部分

参考资料

Scala implicit的更多相关文章

  1. learning scala implicit class

    隐式类可以用来扩展对象的功能非常方便 example: object ImplicitClass_Tutorial extends App { println("Step 1: How to ...

  2. Scala 隐式(implicit)详解

    文章正文 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 1.Spark 中 ...

  3. scala 隐式详解(implicit关键字)

    掌握implicit的用法是阅读spark源码的基础,也是学习scala其它的开源框架的关键,implicit 可分为: 隐式参数 隐式转换类型 隐式调用函数 1.隐式参数 当我们在定义方法时,可以把 ...

  4. 转载:scala中的implicit

    掌握implicit的用法是阅读Spark源码的基础,也是学习Scala其它的开源框架的关键,implicit 可分为: 隐式参数 隐式转换类型 隐式调用函数 1.隐式参数 当我们在定义方法时,可以把 ...

  5. Beginning Scala study note(8) Scala Type System

    1. Unified Type System Scala has a unified type system, enclosed by the type Any at the top of the h ...

  6. Scala比较器:Ordered与Ordering

    在项目中,我们常常会遇到排序(或比较)需求,比如:对一个Person类 case class Person(name: String, age: Int) { override def toStrin ...

  7. A quick tour of JSON libraries in Scala

    A quick tour of JSON libraries in Scala Update (18.11.2015): added spray-json-shapeless libraryUpdat ...

  8. 深入理解Scala的隐式转换系统

    摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码.   使用方式: 1. ...

  9. 了解Scala反射

    本篇文章主要让大家理解什么是Scala的反射, 以及反射的分类, 反射的一些术语概念和一些简单的反射例子. 什么是反射 我们知道, Scala是基于JVM的语言, Scala编译器会将Scala代码编 ...

随机推荐

  1. struts2 Result Type四个常用转跳类型

    Result的四个常用转跳类型分别为 Dispatcher 用来转向页面,是Struts的默认形式 Redirect   重定向到一个URL Chain  用来处理Action链 RedirectAc ...

  2. C#发展历程以及C#6.0新特性

    一.C#发展历程 下图是自己整理列出了C#每次重要更新的时间及增加的新特性,对于了解C#这些年的发展历程,对C#的认识更加全面,是有帮助的. 二.C#6.0新特性 1.字符串插值 (String In ...

  3. PowerShell Script to Deploy Multiple VM on Azure in Parallel #azure #powershell

    Since I need to deploy, start, stop and remove many virtual machines created from a common image I c ...

  4. Socket网络编程--FTP客户端

    Socket网络编程--FTP客户端(1)(Windows) 已经好久没有写过博客进行分享了.具体原因,在以后说. 这几天在了解FTP协议,准备任务是写一个FTP客户端程序.直接上干货了. 0.了解F ...

  5. C# 退出应用程序办法

    Application.Exit();//好像只在主线程可以起作用,而且当有线程,或是阻塞方法的情况下,很容易失灵   this.Close();//只是关闭当前窗体.   Application.E ...

  6. Mysql 慢查询和慢查询日志分析

    众所周知,大访问量的情况下,可添加节点或改变架构可有效的缓解数据库压力,不过一切的原点,都是从单台mysql开始的.下面总结一些使用过或者研究过的经验,从配置以及调节索引的方面入手,对mysql进行一 ...

  7. android Baseadapter 和 ViewHolder的使用

    昨晚学习了徐大神的关于BaseAdapter的讲解,让我受益匪浅特来博客留下印记 说到baseadapter大家一定都不陌生,下面这张图就展示了数据.listview.baseadapter 之间的关 ...

  8. python基础-编码_if条件判断

    一.第一句Python代码 在 /home/dev/ 目录下创建 hello.py 文件,内容如下: [root@python-3 scripts]# cat hello.py #!/usr/bin/ ...

  9. 1025WHERE执行顺序以及MySQL查询优化器

    转自http://blog.csdn.net/zhanyan_x/article/details/25294539 -- WHERE执行顺序-- 过滤比较多的放在前面,然后更加容易匹配,从左到右进行执 ...

  10. 莫比乌斯函数筛法 & 莫比乌斯反演

    模板: int p[MAXN],pcnt=0,mu[MAXN]; bool notp[MAXN]; void shai(int n){ mu[1]=1; for(int i=2;i<=n;++i ...