Scala之隐式参数与隐式转换

隐式(implicit)是scala中最有特色也是最困难的一部分,可以说不掌握隐式就不能算是真正掌握了scala。隐式在许多著名的scala项目中有着广泛的应用,如Spark、GeoMesa等。本文就来详细介绍一下scala中隐式的使用。

scala中的隐式可以分为三部分内容:

  • 隐式参数
  • 隐式转换类型
  • 隐式调用函数 
  • 隐式类 (scala2.10+)


1. 隐式参数

如果scala中的方法的参数使用implicit关键字标识,那么即使在调用方法并没有指定该参数,scala会寻找一个具有跟参数类型相同的变量,并将此变量作为参数传递给该方法,看一个实例:

abstract class Monoid[A] {
  def add(x: A, y: A): A
  def unit: A
}
object ImplicitTest {
  implicit val stringMonoid: Monoid[String] = new Monoid[String] {
    def add(x: String, y: String): String = x concat y
    def unit: String = ""
  }
  
  implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
    def add(x: Int, y: Int): Int = x + y
    def unit: Int = 0
  }
  
  def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
    if (xs.isEmpty) m.unit
    else m.add(xs.head, sum(xs.tail))
    
  def main(args: Array[String]): Unit = {
    println(sum(List(1, 2, 3)))       // uses IntMonoid implicitly
    println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly
  }
}


上例中stringMonoid方法与intMonoid方法在前面加了implicit关键字,意思是该方法是隐式方法。sum方法还有多个参数列表,通过scala科里化方法可以只指定第一个参数xs,那么第二个参数m在不指定的情况下,scala仍然可以根据传入的xs:List[A]中A的具体类型推断出使用stringMonoid方法或者intMonoid方法來赋值给m。这就是隐式参数达到的效果。
 

这里要注意一个很重要的规则:
Scala will first look for implicit definitions and implicit parameters that can be accessed directly (without a prefix) at the point the method with the implicit parameter block is called.

Scala在调用包含有隐式参数块的方法时,将首先查找可以直接访问的隐式定义和隐式参数(无前缀)。


2. 隐式转换

使用隐含转换将变量转换成预期的类型是编译器最先使用 implicit 的地方。这个规则非常简单,当编译器看到类型X而却需要类型Y,它就在当前作用域查找是否定义了从类型X到类型Y的隐式定义。例子:

class Person(val name: String) {
  def talk(person: Person) {
    println(this.name + " speak to " + person.name)
  }
}
 
class Dog(val name: String)


这里定义了两个类:Person和Dog,Person具有一个talk方法,要求参数为Person类型,如过不使用隐式,下面代码会报错:

 var dog = new Dog("wangcai");
 var man = new Person("zhangsan");
 man.talk(dog);

如果使用如下语句,可正常运行:

object convert {
  implicit def dog2person(d: Dog): Person = new Person(d.name)
}
import scala.tutorial.convert._
 
var dog = new Dog("wangcai");
var man = new Person("zhangsan");
man.talk(dog);


因为Dog并不匹配Person类型,scala会自动寻找标记了implicit的Dog=>Person类型的方法,然后进行转换。

3. 隐式调用函数

隐式调用函数可以转换调用方法的对象,比如但编译器看到X .method,而类型 X 没有定义 method(包括基类)方法,那么编译器就查找作用域内定义的从 X 到其它对象的类型转换,比如 Y,而类型Y定义了 method 方法,编译器就首先使用隐含类型转换把 X 转换成 Y,然后调用 Y 的 method。 
例子:

class SwingType {
  def wantLearned(sw: String) = println("兔子已经学会了" + sw)
}
object swimming {
  implicit def learningType(s: ImplicitTest) = new SwingType
}
object ImplicitTest {
import scala.tutorial.swimming._
...
val rabbit = new ImplicitTest
rabbit.wantLearned("蛙泳") //蛙泳
}
输出结果:兔子已经学会了蛙泳

rabbit为ImplicitTest类型,并没有wantLearned方法,但是swimming中有一个Implicit=>SwingType的隐式,而SwingType有wantlearned方法,所以上面程序还是可以正常运行。

4. 隐式类

scala2.10后提供了隐式类,可以使用implicit声明类型,具体要求如下:

  • 1.其所带的构造参数有且只能有一个
  • 2.隐式类必须被定义在类,伴生对象和包对象里
  • 3.隐式类不能是case class(case class在定义会自动生成伴生对象与2矛盾)
  • 4.作用域内不能有与之相同名称的标示符
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方法。

原链接 :https://blog.csdn.net/xiaof22a/article/details/80476646

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值