11

scala_4_再谈隐式转换

 3 years ago
source link: https://zhuanlan.zhihu.com/p/46070103
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

Implicit Design Patterns in Scala​www.lihaoyi.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK