Class Extension

"+9519760513".exists(_.isDigit)

java.lang.String并存在exists方法,为此标准库中在Predef定义了一个隐式转换,使String隐式地转换为StringOps,从而提供更多地操作字符串的方法。

object Predef {
  implicit def augmentString(x: String): StringOps = new StringOps(x)
}

Implicit Resolution Rules

Marking Rule

Only definitions marked implicit are available.

object Predef {
  implicit def intWrapper(x: Int) = new scala.runtime.RichInt(x)
}
object Predef {
  implicit final class any2stringadd[A](private val self: A) extends AnyVal {
    def +(other: String): String = String.valueOf(self) + other
  }
}

Scope Rule

An inserted implicit conversion must be in scope as a single identifier, or be associated with the source or target type of the conversion.

case class Yard(val amount: Int)
case class Mile(val amount: Int)

mile2yard可以定义在object Mile

object Mile {
  implicit def mile2yard(mile: Mile) = new Yard(10*mile.amount)
}

也可以定义在object Yard

object Yard {
  implicit def mile2yard(mile: Mile) = new Yard(10*mile.amount)
}

转换为目标类型时,常常发生如下两个场景:

  • 传递参数时,但类型匹配失败;

def accept(yard: Yard) = println(yard.amount + " yards")
accept(Mile(10))
  • 赋值表达式,但类型匹配失败

val yard: Yard = Mile(10)

Other Rules

  • One-at-a-time Rule: Only one implicit is tried.

  • Explicits-First Rule: Whenever code type checks as it is written, no implicits are attempted.

  • No-Ambiguty Rule: An implicit conversion is only inserted if there is no other possible conversion is inserted.

Where implicits are tried?

  • Conversions to an expected type

    • 传递参数时,但类型匹配失败;

    • 赋值表达式,但类型匹配失败

  • Conversions of the receiver of a selection

    • 调用方法,方法不存在

    • 调用方法,方法存在,但参数类型匹配失败

  • Implicit parameters

Implicit parameters

import scala.math.Ordering

case class Pair[T](first: T, second: T){
  def smaller(implicit order: Ordering[T]) =
    order.min(first, second)
}

TInt

Pair(1, 2).smaller

编译器实际调用:

Pair(1, 2).smaller(Ordering.Int)

其中Ordering.Int定义在Ordering的伴生对象中

object Ordering {
  trait IntOrdering extends Ordering[Int] {
    def compare(x: Int, y: Int) =
      if (x < y) -1
      else if (x == y) 0
      else 1
  }
  implicit object Int extends IntOrdering
}

也就是说

implicitly[Ordering[Int]] == Ordering.Int  // true

其中,implicitly为定义在Predef的一个工具函数,用于提取隐式值

@inline def implicitly[T](implicit e: T) = e

T为自定义类型

import scala.math.Ordering

case class Point(x: Int, y: Int)

object Point {
  implicit object OrderingPoint extends Ordering[Point] {
    def compare(lhs: Point, rhs: Point): Int =
      (lhs.x + lhs.y) - (rhs.x + rhs.y)
  }
}
Pair(Point(0, 0), Point(1, 1)).smaller

等价于

Pair(Point(0, 0), Point(1, 1)).smaller(Point.OrderingPoint)

也就是说

implicitly[Ordering[Point]] == Point.OrderingPoint

Context Bound

import scala.math.Ordering

case class Pair[T : Ordering](first: T, second: T) {
  def smaller(implicit order: Ordering[T]) = order.min(first, second)
}

可以使用implicitly简化

import scala.math.Ordering
case class Pair[T : Ordering](first: T, second: T) {
  def smaller = implicitly[Ordering[T]].min(first, second)
}

可以进一步简化

import scala.math.Ordering

case class Pair[T : Ordering](first: T, second: T) {
  def smaller = Ordering[T].min(first, second)
}

Ordering[T]首先调用了object Orderingapply方法,从而便捷地找到了Order[T]的隐式值

object Ordering {
  def apply[T](implicit ord: Ordering[T]) = ord
}

所以Ordering[T].min等价于implicitly[Ordering[T]].min

View Bound

import scala.math.Ordered

case class Pair[T](first: T, second: T){
  def smaller(implicit order: T => Ordered[T]) = {
    if (order(first) < second) first else second
  }
}

implicit order: T => Ordered[T]smaller的局部作用域内,即是一个隐式参数,又是一个隐式转换函数

import scala.math.Ordered

case class Pair[T](first: T, second: T){
  def smaller(implicit order: T => Ordered[T]) = {
    if (first < second) first else second
  }
}

又因为在Predef预定义了从IntRichInt的隐式转换,而RichIntOrdered[Int]的子类型,所以在Predef定义的implicit Int => RichInt的隐式转换函数可作为隐式参数implicit order: T => Ordered[T]的隐式值。

Pair(1, 2).smaller

等价于

Pair(1, 2).smaller(Predef.intWrapper _)

上述简化的设计,使得隐式参数order没有必要存在,这样的模式较为常见,可归一为一般模式:View Bound

import scala.math.Ordered

case class Pair[T <% Ordered[T]](first: T, second: T) {
  def smaller = if (first < second) first else second
}

需要注意的是:T <% Ordered[T]表示:T可以隐式转换为Ordered[T];而T <: Ordered[T]表示:TOrdered[T]的一个子类型。

Upper Bound

import scala.math.Ordered

case class Pair[T <: Comparable[T]](first: T, second: T) {
  def smaller = if (first.compareTo(second) < 0) first else second
}
Pair("1", "2").smaller  // OK, String is subtype of Comparable[String]
Pair(1, 2).smaller      // Compile Error, Int is not subtype of Comparable[Int]

horance
255 声望29 粉丝

刘光聪,程序员,敏捷教练,开源软件爱好者,具有多年大型遗留系统的重构经验,对OO,FP,DSL等领域具有浓厚的兴趣。


引用和评论

0 条评论