Skip to content

Latest commit

 

History

History
123 lines (80 loc) · 3.72 KB

专题4——隐式.md

File metadata and controls

123 lines (80 loc) · 3.72 KB

专题 4 —— 隐式

一、隐式转换

当调用某个实例方法无相应方法实现。编译器开始寻找有隐式标记的函数字面量或方法,它可以将该实例转换成目标实例(可以调用目标方法的实例)

定义如下表达式

List(1, 2, 3) <= List(4, 5)

编译器

List 没有 <= 方法,无相关隐式转换,报错。

解决

经查询 Ordered[List[A]] 有该方法。故定义一个隐式转换方法,如下

import scala.language.implicitConversions

implicit def list2ordered[A](x: List[A])(implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] =
  new Ordered[List[A]] {
    //replace with a more useful implementation
    def compare(that: List[A]): Int = 1
  }

编译器

List 没有 <= 方法,查询 List 类型相关的隐式转换。发现 list2ordered 符合要求,调用 list2ordered(List(1, 2, 3)) 完成转换 Ordered[List[Int]],再调用它的 <= 方法。

补充说明

list2ordered 定义了一个隐式参数,参数类型是一个函数 Int => Ordered[Int]。编译器在隐式转换时,调用 list2ordered 方法,缺少另一个隐式参数 elem2ordered

编译器开始自动寻找符合该参数类型的隐式标记。发现 implicit def intWrapper(x: Int) = new runtime.RichInt(x) 符合条件,因此传入 intWrapper 作为第二个参数。

二、使用规则及使用场景

  • 规则

    标记:implicit 可以标记变量、函数或对象
    范围:finding-implicits
    一次:只会试图转换一次
    备选项

  • 场景

    隐式参数
    隐式转换

三、隐式参数

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
  }
}

分析

调用 sum 函数时,发现少了一个参数 m。这个参数被标记为 implicit ,并且其类型为 Monoid[Int]。接下来它开始在当前作用域寻找类型为 Monoid[Int]的隐式定义或具有隐式标记的成员。发现隐式常量 intMonoid 满足条件,将其作为参数传入 sum 函数中。

隐式参数本质是一个参数,只不过它不需要主动传参,编译器会自动传递相应参数。它需要满足静态类型检查要求。即对传入的隐式变量可能会有一些限制,比如同一方法签名中相同的泛型变量必须保持一致。

视图边界

语法糖

def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod

上下文边界

语法糖

def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)

举例:

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)