摘要:
通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码。
 
使用方式:
1.将方法或变量标记为implicit
2.将方法的参数列表标记为implicit
3.将类标记为implicit
 
Scala支持两种形式的隐式转换:
隐式值:用于给方法提供参数
隐式视图:用于类型间转换或使针对某类型的方法能调用成功
 
隐式值:
 例1:声明person方法。其参数为name,类型String
  1. scala> def person(implicit name : String) = name //name为隐式参数
  2. person: (implicit name: String)String
直接调用person方法
  1. scala> person
  2. <console>:9: error: could not find implicit value for parameter name: String
  3. person
  4. ^
报错!编译器说无法为参数name找到一个隐式值
定义一个隐式值后再调用person方法
  1. scala> implicit val p = "mobin" //p被称为隐式值
  2. p: String = mobin
  3. scala> person
  4. res1: String = mobin
因为将p变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺少参数。
但是如果此时你又在REPL中定义一个隐式变量,再次调用方法时就会报错
  1. scala> implicit val p1 = "mobin1"
  2. p1: String = mobin1
  3. scala> person
  4. <console>:11: error: ambiguous implicit values:
  5. both value p of type => String
  6. and value p1 of type => String
  7. match expected type String
  8. person
  9. ^
匹配失败,所以隐式转换必须满足无歧义规则,在声明隐式参数的类型是最好使用特别的或自定义的数据类型,不要使用Int,String这些常用类型,避免碰巧匹配
 
隐式视图
隐式转换为目标类型:把一种类型自动转换到另一种类型
例2:将整数转换成字符串类型:
  1. scala> def foo(msg : String) = println(msg)
  2. foo: (msg: String)Unit
  3.  
  4. scala> foo(10)
  5. <console>:11: error: type mismatch;
  6. found : Int(10)
  7. required: String
  8. foo(10)
  9. ^
显然不能转换成功,解决办法就是定义一个转换函数给编译器将int自动转换成String
  1. scala> implicit def intToString(x : Int) = x.toString
  2. intToString: (x: Int)String
  3.  
  4. scala> foo(10)
  5. 10
 
隐式转换调用类中本不存在的方法
例3:通过隐式转换,使对象能调用类中本不存在的方法
  1. class SwingType{
  2. def wantLearned(sw : String) = println("兔子已经学会了"+sw)
  3. }
  4. object swimming{
  5. implicit def learningType(s : AminalType) = new SwingType
  6. }
  7. class AminalType
  8. object AminalType extends App{
  9. import com.mobin.scala.Scalaimplicit.swimming._
  10. val rabbit = new AminalType
  11. rabbit.wantLearned("breaststroke") //蛙泳
  12. }
编译器在rabbit对象调用时发现对象上并没有wantLearning方法,此时编译器就会在作用域范围内查找能使其编译通过的隐式视图,找到learningType方法后,编译器通过隐式转换将对象转换成具有这个方法的对象,之后调用wantLearning方法
 
可以将隐式转换函数定义在伴生对象中,在使用时导入隐式视图到作用域中即可(如例4的learningType函数)
 
还可以将隐式转换函数定义在凶对象中,同样在使用时导入作用域即可,如例4
例4:
  1. class SwingType{
  2. def wantLearned(sw : String) = println("兔子已经学会了"+sw)
  3. }
  4.  
  5. package swimmingPage{
  6. object swimming{
  7. implicit def learningType(s : AminalType) = new SwingType //将转换函数定义在包中
  8. }
  9. }
  10. class AminalType
  11. object AminalType extends App{
  12. import com.mobin.scala.Scalaimplicit.swimmingPage.swimming._ //使用时显示的导入
  13. val rabbit = new AminalType
  14. rabbit.wantLearned("breaststroke") //蛙泳
  15. }
像intToString,learningType这类的方法就是隐式视图,通常为Int => String的视图,定义的格式如下:
     implicit def  originalToTarget (<argument> : OriginalType) : TargetType
其通常用在于以两种场合中:
1.如果表达式不符合编译器要求的类型,编译器就会在作用域范围内查找能够使之符合要求的隐式视图。如例2,当要传一个整数类型给要求是字符串类型参数的方法时,在作用域里就必须存在Int => String的隐式视图
 
2.给定一个选择e.t,如果e的类型里并没有成员t,则编译器会查找能应用到e类型并且返回类型包含成员t的隐式视图。如例3
 
隐式类:
在scala2.10后提供了隐式类,可以使用implicit声明类,但是需要注意以下几点:
1.其所带的构造参数有且只能有一个
2.隐式类必须被定义在类,伴生对象和包对象里
3.隐式类不能是case class(case class在定义会自动生成伴生对象与2矛盾)
4.作用域内不能有与之相同名称的标示符
 
例5:
  1. object Stringutils {
  2. implicit class StringImprovement(val s : String){ //隐式类
  3. def increment = s.map(x => (x +1).toChar)
  4. }
  5. }
  6. object Main extends App{
  7. import com.mobin.scala.implicitPackage.Stringutils._
  8. println("mobin".increment)
  9. }
编译器在mobin对象调用increment时发现对象上并没有increment方法,此时编译器就会在作用域范围内搜索隐式实体,发现有符合的隐式类可以用来转换成带有increment方法的StringImprovement类,最终调用increment方法。
 

隐式转换的时机:

1.当方法中的参数的类型与目标类型不一致时

 
2.当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换
 
隐式解析机制
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
1.首先会在当前代码作用域下查找隐式实体(隐式方法  隐式类 隐式对象)
 
2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找
类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:
    (1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索
    (2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的
伴生对象和String的伴生对象
    (3) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索
    (4) 如果T是个类型注入S#T,那么S和T都会被搜索
 
隐式转换的前提:
1.不存在二义性(如例1)
 
2.隐式操作不能嵌套使用(如 convert1(covert2(x)))+y
 
3.代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式黑铁

转载:深入理解Scala的隐式转换系统的更多相关文章

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

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

  2. 深入理解Scala的隐式转换

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

  3. scala自定义隐式转换

    Scala自定义隐式转换 一.编写隐式转换类 /** * Author Mr. Guo * Create 2019/4/20 - 17:40 */ object StringImprovments { ...

  4. Scala学习——隐式转换

    scala隐式转换 一.需求:为一个类添加一个新的方法 java:动态代理 scala:隐式转换 隐式转换例子: 1.man to superMan package top.ruandb.scala. ...

  5. Scala模式匹配| 隐式转换

    1. 模式匹配 Scala中的模式匹配类似于Java中的switch语法,但是更加强大.模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分 ...

  6. Scala之隐式转换

    概述 简单说,隐式转换就是:当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型. 隐式转换有四种常见的使用场景: 将某一类 ...

  7. Scala之隐式转换implicit详解

    假设我们有一个表示文本的行数的类LineNumber: class LineNumber ( val num : Int ) 我们可以用这个类来表示一本书中每一页的行数: val lineNumOfP ...

  8. Scala学习之路 (八)Scala的隐式转换和隐式参数

    一.概念 Scala 2.10引入了一种叫做隐式类的新特性.隐式类指的是用implicit关键字修饰的类.在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换. 隐式转换和隐式参数是Scal ...

  9. scala的隐式转换

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

随机推荐

  1. Android-ContentProvider使用

    Android-ContentProvider使用 一 建立ContentProviderserver端 1 建立一个继承自ContentProvider的类并重写接口方法(这里仅打一些log做代表) ...

  2. Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件

    一.问题描述 今天使用SDK Manager将Android SDK的版本更新到了Android 5.1的版本,eclipse创建android项目时,预览activity_main.xml文件时提示 ...

  3. MySQL中的insert ignore into, replace into用法总结

    MySQL replace into 有三种形式: 1. replace into tbl_name(col_name, ...) values(...) 2. replace into tbl_na ...

  4. java中常用jar包

    commons-io.jar:可以看成是java.io的扩展,用来帮助进行IO功能开发.它包含三个主要的领域:Utilityclasses-提供一些静态方法来完成公共任务.Filters-提供文件过滤 ...

  5. 在Razor中输出Html的两种方式

    Razor中所有的Html都会自动编码,这样就不需要我们手动去编码了(安全),但在需要输出Html时就是已经转义过的Html文本了,如下所示: @{ string thisTest = "& ...

  6. maven本地仓库中存在jar包,但编译不成功,显示jar包不存在

    介绍一下背景,项目要迁移进坑人的离线的内网开发,将在同事那编译通过的代码和maven仓库拷进内网,打算编译通过之后再上传私服,结果配好maven之后,本地库中的部分jar包显示没有引入,如下面的波浪线 ...

  7. GIT无法自动忽略YellowRV1.1.uvgui.Administrator文件的解决方法

    原来这个YellowRV1.1.uvgui.Administrator文件是在.gitignore之前被提交进服务器的,.gitignore对已经存在服务器里的文件是不起作用的.按照回答里投票最高的答 ...

  8. HTML5手机页面里面如何把长按复制避免

    在写HTML5手机页面的时候,有时候会写到一些标签是需要用户长按然后放开的 但是微信里面长按就会出现复制,大大影响了用户体验,那么如何可以避免呢? 我也是最近写到这样的页面,总结了一部分,大家可以作为 ...

  9. Atitit 常见每日流程日程日常工作.docx v4

    Atitit 常见每日流程日程日常工作.docx v4 ----早晨 签到 晨会,每天或者隔天 每日计划( )项目计划,日常计划等. mailbox读取检查 每日趋势 推库 -----下午 签退 每日 ...

  10. MySQL四种事务隔离级别详解

    本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做 ...