分享

scala的implicit和magnet模式

gefieder 发表于 2015-4-26 22:19:00 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 26278
问题导读:
1、什么是scala?
2、什么是magnet模式?
3、ExecutionContext的获取方法有?




对初学scala的人,implicit像一个黑魔法,来无影去无踪,像它的名字一样非常“含蓄”。
从某种意义上讲,implicit是一个类型系统的游戏。scala是强类型系统,所有的参数都需要符合类型预期,如果需要一个Int类型,你传来一个String,编译器会报类型不符错误。implicit的引入,使在报错之前还有一次机会,即:如果编译器在当前作用域内找到一个从String转换到Int的implicit定义函数,编译器会用这个implicit把你传给它的String转换成它需要的Int,于是一切又愉快的发生下去了。
当然这只是implicit的一种使用场景,spray.io(已合并到akka-http)的magnet模式利用的就是这个特性。
magnet模式magnet模式简单讲就是通过定义一个magnet类型作为统一的参数,然后针对需要重载的参数列表,类型等,在magnet类型的companion object中实现相应的转换为magnet类型的implitcit函数。
如:可以定义一个Magnet类型实现一个接受任意参数的add函数
  1. def add(magnet: MyMagnet): magnet.Result = magnet()
  2. sealed trait MyMagnet {
  3.   type Result
  4.   def apply(): Result
  5. }
  6. object MyMagnet {
  7.   //一个整形参数到MyMagenet的转换
  8.   implicit def fromInt(i: Int) =
  9.     new MyMagnet {
  10.       type Result = Int
  11.       def apply(): Result = i + 1
  12.     }
  13.   //一个String参数到MyMagenet的转换
  14.   implicit def fromString(s: String) =
  15.     new MyMagnet {
  16.       type Result = String
  17.       def apply(): Result = "hello " + s
  18.     }
  19.   //一个String参数加一个整形参数到MyMagenet的转换
  20.   implicit def fromStringAndInt(tuple: (String, Int)) =
  21.     new MyMagnet {
  22.       type Result = String
  23.       def apply(): Result = tuple._1 + tuple._2.toString
  24.     }
  25. }
复制代码

调用的时候可以:
  1. scala> add(1)
  2. res9: Int = 2
  3. scala> add("world")
  4. res10: String = hello world
  5. scala> add("happy string ", 5)
  6. res11: String = happy string 5
复制代码

看到这里,你可能会说这不就是重载吗?java和scala原生就支持重载,但jvm对泛型(generics)的支持是通过类型擦除(type erasure)实现的,这意味着java常规的重载无法带类型参数,如:jvm无法区分下面这种类型不同的List参数。
  1. scala> :paste
  2. // Entering paste mode (ctrl-D to finish)
  3. def add(a: List[Int]): Unit = {}
  4. def add(a: List[String]): Unit = {}
  5. // Exiting paste mode, now interpreting.
  6. <console>:8: error: double definition:
  7. def add(a: List[Int]): Unit at line 7 and
  8. def add(a: List[String]): Unit at line 8
  9. have same type after erasure: (a: List)Unit
  10. def add(a: List[String]): Unit = {}
复制代码

而magnet模式正好可以弥补这个缺憾,另外magnet模式相当于把重载的实现从语言层面拉到了自己的代码逻辑中,有利于针对性的引入一些新技巧减少冗余代码。当然,magnet的缺点也是很明显的:额外的一层增加了代码的复杂度。
隐含参数implicit另外一个常用的场景是: 替代全局变量,作为某个执行上下文中的隐含参数。
如:scala中异步的一个重要方法是使用Future。Futrure语义清晰,使用优雅,比手动起线程不知道高到哪里去了;),但Future在后台其实还是通过线程来执行的,要用Future就需要一个指定的执行上下文环境(ExecutionContext ,一般是线程池)来跑Future。Future又是一个object(单例对象,不是普通类)没有地方放这个线程池的引用,解决方案只能是在所有Future的方法中加上ExecutionContext参数,方法很函数式,但接口略显冗余。好在scala有implicit,只要你调用Future时,上下文中有一个implicit的ExecutionContext变量,Future会自动在这个EC上跑代码。
所以scala的Future方法都有一个(implicit executor: ExecutionContext)参数
def onComplete(U &#8658; U)(implicit executor: ExecutionContext): Unit
不同于全局变量,你在调用Future方法时,想使用某个指定的ExecutionContext,还是可以把它作为参数显示的传递给Future方法,这个显示传递的参数会覆盖implicit的参数。
另:ExecutionContext的获取方法有
  • 直接引用全局EC。import scala.concurrent.ExecutionContext.Implicits.global
  • akka的actor中,引用当前actor系统的EC。import context.dispatcher
  • 也可以手动创建一个独占使用,确保线程池里的线程不会被其他不相干任务耗尽。

  1.    import java.util.concurrent.Executors
  2.    import concurrent.ExecutionContext
  3.    //创建一个4个线程的线程池
  4.    val executorService = Executors.newFixedThreadPool(4)
  5.    implicit val ec = ExecutionContext.fromExecutorService(executorService)
复制代码

资料来源:http://xun.im/2015/04/21/scala-implicit-and-magnet-pattern/



欢迎加入about云群371358502、39327136,云计算爱好者群,亦可关注about云腾讯认证空间||关注本站微信

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条