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个约束和一个注解问题:
- 必须要有主一个构造函数且只能一个构造参数(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还有其他作用和局限性,可以参考上面链接。如果发现错误,请指出,先谢过。