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

摘要:

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

使用方式:

1.将方法或变量标记为implicit

2.将方法的参数列表标记为implicit

3.将类标记为implicit

Scala支持两种形式的隐式转换:

隐式值:用于给方法提供参数

隐式视图:用于类型间转换或使针对某类型的方法能调用成功

隐式解析机制

即编译器是如何查找到缺失信息的,解析具有以下两种规则:

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:声明person方法。其参数为name,类型String

scala> def person(implicit name : String) = name   //name为隐式参数
person: (implicit name: String)String

直接调用person方法

scala> person
<console>:9: error: could not find implicit value for parameter name: String
              person
              ^

报错!编译器说无法为参数name找到一个隐式值

定义一个隐式值后再调用person方法

scala> implicit val p = "mobin"   //p被称为隐式值
p: String = mobin
scala> person
res1: String = mobin

因为将p变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺少参数。

但是如果此时你又在REPL中定义一个隐式变量,再次调用方法时就会报错

scala> implicit val p1 = "mobin1"
p1: String = mobin1
scala> person
<console>:11: error: ambiguous implicit values:
 both value p of type => String
 and value p1 of type => String
 match expected type String
              person
              ^

匹配失败,所以隐式转换必须满足无歧义规则,在声明隐式参数的类型是最好使用特别的或自定义的数据类型,不要使用Int,String这些常用类型,避免碰巧匹配

隐式视图

隐式转换为目标类型:把一种类型自动转换到另一种类型

例2:将整数转换成字符串类型:

scala> def foo(msg : String) = println(msg)
foo: (msg: String)Unit

scala> foo(10)
<console>:11: error: type mismatch;
found : Int(10)
required: String
foo(10)
^

显然不能转换成功,解决办法就是定义一个转换函数给编译器将int自动转换成String

scala> implicit def intToString(x : Int) = x.toString
intToString: (x: Int)String

scala> foo(10)
10

隐式转换调用类中本不存在的方法

例3:通过隐式转换,使对象能调用类中本不存在的方法

class SwingType{
  def  wantLearned(sw : String) = println("兔子已经学会了"+sw)
}
object swimming{
  implicit def learningType(s : AminalType) = new SwingType
}
class AminalType
object AminalType extends  App{
  import com.mobin.scala.Scalaimplicit.swimming._
  val rabbit = new AminalType
    rabbit.wantLearned("breaststroke")         //蛙泳
}

编译器在rabbit对象调用时发现对象上并没有wantLearning方法,此时编译器就会在作用域范围内查找能使其编译通过的隐式视图,找到learningType方法后,编译器通过隐式转换将对象转换成具有这个方法的对象,之后调用wantLearning方法

可以将隐式转换函数定义在伴生对象中,在使用时导入隐式视图到作用域中即可(如例4的learningType函数)

还可以将隐式转换函数定义在凶对象中,同样在使用时导入作用域即可,如例4

例4:

class SwingType{
  def  wantLearned(sw : String) = println("兔子已经学会了"+sw)
}

package swimmingPage{
object swimming{
  implicit def learningType(s : AminalType) = new SwingType  //将转换函数定义在包中
  }
}
class AminalType
object AminalType extends  App{
  import com.mobin.scala.Scalaimplicit.swimmingPage.swimming._  //使用时显示的导入
  val rabbit = new AminalType
    rabbit.wantLearned("breaststroke")         //蛙泳
}

像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:

object Stringutils {
  implicit class StringImprovement(val s : String){   //隐式类
      def increment = s.map(x => (x +1).toChar)
  }
}
object  Main extends  App{
  import com.mobin.scala.implicitPackage.Stringutils._
  println("mobin".increment)
}

编译器在mobin对象调用increment时发现对象上并没有increment方法,此时编译器就会在作用域范围内搜索隐式实体,发现有符合的隐式类可以用来转换成带有increment方法的StringImprovement类,最终调用increment方法。

隐式转换的时机:

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

2.当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换

隐式转换的前提:

1.不存在二义性(如例1)

2.隐式操作不能嵌套使用(如 convert1(covert2(x)))+y

3.代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式黑铁

时间: 2024-10-23 14:09:45

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

转载:深入理解Scala的隐式转换系统

摘要: 通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码. 使用方式: 1.将方法或变量标记为implicit 2.将方法的参数列表标记为implicit 3.将类标记为implicit Scala支持两种形式的隐式转换: 隐式值:用于给方法提供参数 隐式视图:用于类型间转换或使针对某类型的方法能调用成功 隐式值: 例1:声明person方法.其参数为name,类型String

scala自定义隐式转换

Scala自定义隐式转换 一.编写隐式转换类 /** * Author Mr. Guo * Create 2019/4/20 - 17:40 */ object StringImprovments { implicit class StringImprove(s: String) { def increment = s.toString.map(c => (c + 1).toChar) } implicit class Intc(i: Int) { def xx = { Integer.pars

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

博客核心内容: 1.Scala中的两种隐式转换机制以及隐式视图的定义方式 2.Scala中的隐式绑定可能所处的位置以及如何更好的使用隐式转换 3.Scala中的隐式转换相关操作规则 4.Scala中的隐式参数 5.Scala中的隐式类 6.Scala中的隐式对象 7.Scala中的两种隐式类型约束(结合Scala中的类型系统) 8.Predef类中的implicitly方法的用法介绍以及Ordering类型转化为Oredered类型的方式 1.Scala中的两种隐式转换机制以及隐式视图的定义方式

IDEA的auto import 与 Scala的隐式转换

设置IDEA的auto import 之后,编写Spark应用程序时的一些自动导入情况及其说明. 1.普通类的自动导入 var cnames = new ArrayBuffer[String]() var ips = new ArrayBuffer[String]() 直接根据提示alt+enter便可以导入ArrayBuffer类包 2.需要隐式转换 val joinRDD = reqRDD.join(respRDD).map(x => (x._2._1, x._2._2)).reduceBy

Scala之隐式转换implicit详解

假设我们有一个表示文本的行数的类LineNumber: class LineNumber ( val num : Int ) 我们可以用这个类来表示一本书中每一页的行数: val lineNumOfPage1 = new LineNumber(112) val lineNumOfPage2 = new LineNumber(120) 上面的代码分别表示了第一页和第二页的行数.当然,我们也应该可以将它们相加,得到这两页的总行数: val totalLineNum = lineNumOfPage1

Scala特性: 隐式转换

1.隐式转换特征: 1)隐式参数的用法 · 获取可能的预期类型 · 获取预期类型,并且拥有预期类型的行为 · 对信息进行补充说明(一般用函数做隐式参数的比较多) 2)隐式类: 3)隐式method:

scala学习笔记-隐式转换与隐式参数(18)

Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象.通过这些功能,可以实现非常强大,而且特殊的功能. Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function.定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用.Scala会根据隐式转换函数的签名,在程序中使用到隐式转换函数接收的参数类型定义的对象时,会自动将其传入隐式转

Scala隐式转换类遇到的问题

今天练习Scala的隐式转换类遇到的一个问题,测试代码如下: object ImplcitTest { def main(args: Array[String]) { import Context._ val person1 = User("zhangsan") println(person1.getStr()) val filePath = Thread.currentThread.getContextClassLoader.getResource("test.txt&qu

隐式转换与隐式参数

Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象.通过这些功能,可以实现非常强大,而且特殊的功能. Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function.定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用.Scala会根据隐式转换函数的签名,在程序中使用到隐式转换函数接收的参数类型定义的对象时,会自动将其传入隐式转