scala_4_再谈隐式转换

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)

//3. 使用目标方法
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个约束和一个注解问题:

  1. 必须要有主一个构造函数且只能一个构造参数(implicit参数除外)。构造参数就是源类型. 这个构造函数即等价上面第2步的隐式转换方法:
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!

//cuz case class has companion object by default 
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还有其他作用和局限性,可以参考上面链接。如果发现错误,请指出,先谢过。

more blog post about scala implicit


编辑于 2019-02-25 23:11