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)
}
當T
為Int
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 Ordering
的apply
方法,從而便捷地找到了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
預定義了從Int
到RichInt
的隱式轉換,而RichInt
是Ordered[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]
表示:T
是Ordered[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]