Scala之隐式转换

在java中,我们如果需要为某个特定的类新增一个方法,即:功能增强,大致有几种方式:

  1. 继承
  2. 装饰器
  3. 代理(静态动态)

那么,在scala中,这个就可以使用隐式转换来达到。

隐式转换

大致就是为类添加新方法,大致流程:

  1. 为某一个特定的类Dog
  2. 写一个增强类RichDog(里面添加新方法)
  3. 写一个隐式声明 implicit def dogToRichDog(dog:Dog):RichDog = new RichDog(dog.name),
  4. 在特定的类需要使用到新方法之前,import 这个隐式声明之后就可以get到

对类功能进行增强,表面看不出来

比如:RDD.scala 源文件中就有大量的隐式转换,常见的reduceByKey方法 算子 其实本身不是自己定义的,而是增强而来

两个案例

案例一:增强自己定义的类

简单说明:Dog本身没有speak方法,但是这里的话,需要隐式声明一下 :

implicit def dogToRichDog(dog:Dog):RichDog = new RichDog(dog.name)

这样Dog就能使用RichDog里面的方法,scala中其实RichDog本不是Dog的子类,但是import 这个隐式声明之后就可以get到

相当于父类调用子类对象,java可以直接用,这里需要手动import 隐式声明

// 隐式转换: 偷偷的增强,为类添加新的方法,表面看不出来
object ImplicitDogApp {
def main(args:Array[String]): Unit = {

implicit def dogToRichDog(dog:Dog):RichDog = new RichDog(dog)

val dog = new Dog("小奶狗")
dog.speak()

}

}
class Dog(val name:String){

}
class RichDog(val dog:Dog){


def speak() = {
println(s"你好,我的名字是:${dog.name}")
}
}

案例二 增强java.io.File

简单说明:java.io.File 这个类本身没有read这个方法,通过自己编写一个RichFile 增强File的功能

import java.io.File
import scala.io.Source

object ImplicitFileApp {
def main(args:Array[String]): Unit ={
import ImplicitAspect.fileToRichFile

val file = new File("f:/hello.txt")
val context = file.read()
println(context)

}

}
class RichFile(val file:File) {

def read(): String ={
Source.fromFile(file.getPath).mkString
}

}

注意上面的是: import ImplicitAspect.fileToRichFile ,其实这里是写了一个隐式转换的切面类 的意思,当你的隐式声明变多之后,这样方便统一管理。定义的切面类:ImplicitAspect 如下:

import java.io.File

object ImplicitAspect {
implicit def fileToRichFile(file:File):RichFile = new RichFile(file)
implicit def dogToRichDog(dog:Dog):RichDog = new RichDog(dog)
}

注意事项:

  1. 隐式转换函数的函数名可以是任意的,与函数名称无关,只与函数签名(函数参数和返回值类型)有关。

  2. 如果当前作用域中存在函数签名相同但函数名称不同的两个隐式转换函数,则在进行隐式转换时会报错。

隐式参数

隐式参数: 生产上不建议使用,指的是在函数或者方法中,定义一个用implicit修饰的参数,此时:scala会尝试找到一个指定类型的,用implicit 修饰的对象,即 隐式值,并注入参数

object ImplicitParamApp {
def main(args: Array[String]): Unit = {
def testParam(implicit name:String): Unit = {
println(name + "~~~~~~~~~~~~")
}
// 测试1. 在没有定义 implicit val name 之前,需要显示的进行传参
testParam("a")

// 测试2.定义一个用implicit修饰的参数
implicit val name1 = "留歌36"
// 这时,不用传递参数,测试OK
testParam

// 测试3:定义多个String类型的implicit修饰的参数
implicit val name2 = "留歌36"
implicit val name3 = "留歌37"
// 测试不Ok,因为编译器不知道该使用哪一个
testParam

}
}

隐式参数使用的常见问题:

  1. 当函数没有柯里化时,implicit关键字会作用于函数列表中的的所有参数。

  2. 隐式参数使用时要么全部不指定,要么全不指定,不能只指定部分。

  3. 同类型的隐式值只能在作用域内出现一次,即不能在同一个作用域中定义多个相同类型的隐式值。

  4. 在指定隐式参数时,implicit 关键字只能出现在参数开头。

  5. 如果想要实现参数的部分隐式参数,只能使用函数的柯里化,

    如要实现这种形式的函数,def test(x:Int, implicit y: Double)的形式,必须使用柯里化实现:def test(x: Int)(implicit y: Double).

  6. 柯里化的函数, implicit 关键字只能作用于最后一个参数。否则,不合法。

  7. implicit 关键字在隐式参数中只能出现一次,柯里化的函数也不例外!

  8. 匿名函数不能使用隐式参数

  9. 柯里化的函数如果有隐式参数,则不能使用其偏应用函数

隐式类

是指对类增加 implicit 关键字

作用:对类的增强

object ImplicitClassApp {
def main(args: Array[String]) : Unit = {

// class Calculator(x:Int)这个类中 对于Int 类型的值,我们都是可以拥有 本个类中定义的方法的
implicit class Calculator(x:Int){
def add(a:Int)={
a + x
}
}

println(1.add(3))

}
}

注意事项: 隐式类的主构造函数参数有且仅有一个!之所以只能有一个参数,是因为隐式转换是将一种类型转换为另外一种类型,源类型与目标类型是一一对应的

Author: Tunan
Link: http://yerias.github.io/2020/12/15/scala/5/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.