scala_4_再談隱式轉換
目錄
- implicit class
- implicitly method
- value class and pimp my class pattern
- type class
implicit class
在第二篇文章裡提過隱式轉換,如果我們想要給某個型別增加方法,通過隱式轉換需要兩步:
//1. 定義一個含有目標方法的class class BlingString(s:String) { def bling = "*"+s+"*" } //2. 定義隱式轉換方法 implicit def str2BlingString(s:String) = new BlingString(s) val s = "hello" s.bling // *hello*
可以看到第2步非常的冗餘,於是SIP-13提出一個 implicit class
,將上面的2步合併:
implicit class BlingString(s:String) { def bling = "*"+s+"*" } //implicit def str2BlingString(s:String) = new BlingString(s) val hi = "hello" hi.bling // *hello*
注意,這個只是一個語法糖。去糖後就是上面的那個形式。 implicit class有3個約束和一個註解問題:
- 必須要有主一個建構函式且只能一個構造引數(implicit引數除外)。這個建構函式的作用就是用於隱式轉換方法:
implicit class RichDate(date: java.util.Date) // OK! implicit class Indexer[T](collecton: Seq[T], index: Int) // BAD! implicit class Indexer[T](collecton: Seq[T])(implicit index: Index) // OK!
2. 只能定義在其他trait/class/object中:
object Helpers { implicit class RichInt(x: Int) // OK! } implicit class RichDouble(x: Double) // BAD!
3. 在當前scope內,不允許有和implicit class同名的方法,物件,變數。因為case class會自動生成同名object物件,所以implicit class不能是case class。
object Bar implicit class Bar(x: Int) // BAD! val x = 5 implicit class x(y: Int) // BAD! implicit case class Baz(x: Int) // BAD! conflict with the companion object
4.還有就是implicit class的註解在去語法糖後會自動新增到類和方法,除非在註解中指明範圍:
@bar implicit class Foo(n: Int) //desugar @bar implicit def Foo(n: Int): Foo = new Foo(n) @bar class Foo(n:Int) //除非在註解中指明:genClass / method @(bar @genClass) implicit class Foo(n: Int) //desugar得到 @bar class Foo(n: Int) implicit def Foo(n: Int): Foo = new Foo(n)
implicitly
scala的PreDef中有有一個implicitly方法,表示在當前scope徵召一個隱式變數。
//PreDef @inline def implicitly[T](implicit e: T) = e
implitly[T] means return implicit value of type T in the context
implicit class Foo(val i: Int) { def addValue(v: Int): Int = i + v } implicit val foo:Foo = Foo(1) val fooImplicitly = implicitly[Foo] // Foo(1)
value class
scala 還有一個概念:value class
class Wrapper(val underlying: Int) extends AnyVal //1. 一個public val引數表示runtime型別,這裡是Int. 編譯時是Wrapper型別,所以value class目的是降低分配開銷。 //2. value class 需要 extends AnyVal //3. value class 只能有 defs, 不能有vals, vars, or nested traits, classes or objects, //因為def是通過靜態方法實現的,而val,var這些則必須建立相應型別了。 //4. value class 只能擴充套件通用trait(universal traits), //universal traits是A universal trait is a trait that extends Any, only has defs as members, and does no initialization. //
extension method
當implicit class型別引數是AnyVal子類時,value class和上面的implicit class形式相近,所以可以通過value class降低implicit class的分配開銷。例如RichtInt
implicit class RichInt(val self: Int) extends AnyVal { def toHexString: String = java.lang.Integer.toHexString(self) }
因為RichInt是value class,在執行時(runtime)不會有RichInt這個類,而是Int,而 3.toHexString
實際是通過靜態方法實現的: RichInt$.MODULE$.extension$toHexString(3)
,這麼做好處是減少物件分配開銷(avoid the overhead of allocation)。如果implicit class的型別引數不是AnyVal子類,那麼在runtime時會有相應型別物件被建立,使用者察覺不到區別。
value class還有其他作用和侷限性,可以參考上面連結。如果發現錯誤,請指出,先謝過。