加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Mixin包装Scala特征的每个方法

发布时间:2020-12-16 10:00:44 所属栏目:安全 来源:网络整理
导读:假设我有几个方法的特性Foo.我想创建一个新的特性,扩展Foo但“包装”每个方法调用,例如使用一些print语句(实际上这将是更复杂的东西/我有几个不同的用例). trait Foo { def bar(x: Int) = 2 * x def baz(y: Int) = 3 * y} 我可以通过覆盖每个方法手动完成此
假设我有几个方法的特性Foo.我想创建一个新的特性,扩展Foo但“包装”每个方法调用,例如使用一些print语句(实际上这将是更复杂的东西/我有几个不同的用例).

trait Foo {
  def bar(x: Int) = 2 * x
  def baz(y: Int) = 3 * y
}

我可以通过覆盖每个方法手动完成此操作.但这似乎不必要地冗长(并且很容易调用错误的超级方法):

object FooWrapped extends FooWrapped
trait FooWrapped extends Foo {
  override def bar(x: Int) ={
    println("call")
    super.bar(x)
  }
  override def baz(y: Int) ={
    println("call")
    super.baz(y)
  }
}

scala> FooWrapped.bar(3)
call
res3: Int = 6

我希望写一个mixin特性,我可以重用其他特征,并用作:

trait FooWrapped extends Foo with PrintCall

这样我就不必手动覆盖每个方法(mixin会为我做这个).

是否有可能在Scala中编写这样的mixin特性?它会是什么样子?

解决方法

更新这里是宏.由于quasiquotes,它比我想象的要痛苦得多.他们真棒.这段代码只做了一点,你可能需要改进它.它可能无法解释某些特殊情况.此外,它假设父类和它的方法都没有类型参数,它只包含给定类或特征的方法,但不包括它的父方法,如果你有辅助构造函数等它可能无法工作.但我希望它会给你一个关于如何满足您的特定需求的想法,让它在所有情况下工作不幸的是现在对我来说太大了.

object MacrosLogging {
  import scala.language.experimental.macros
  import scala.reflect.macros.blackbox


  def log_wrap[T](): T = macro log_impl[T]
  def log_impl[T : c.WeakTypeTag](c: blackbox.Context)(): c.Expr[T] = {
    import c.universe._

    val baseType = implicitly[c.WeakTypeTag[T]].tpe
    val body = for {
       member <- baseType.declarations if member.isMethod && member.name.decodedName.toString != "$init$"
      method = member.asMethod
      params = for {sym <- method.paramLists.flatten} yield q"""${sym.asTerm.name}: ${sym.typeSignature}"""
      paramsCall = for {sym <- method.paramLists.flatten} yield sym.name
      methodName = member.asTerm.name.toString
    } yield  {
      q"""override def ${method.name}(..$params): ${method.returnType} = { println("Method " + $methodName + " was called"); super.${method.name}(..$paramsCall); }"""
    }

    c.Expr[T] {q""" { class A extends $baseType { ..$body }; new A } """}
  }
}

如果您不想创建实例,但是您确实只想为您的特性添加日志记录以便进一步混合,则可以使用相对相同的代码执行此操作,但使用宏天堂类型注释:http://docs.scala-lang.org/overviews/macros/annotations这些允许您标记您的类定义并在定义内执行修改

你可以用Dynamic做一些你想要的东西,但有一个问题 – 你无法使它成为原始类型,所以它不是一个混合.动态仅在类型检查失败时才开始工作,因此您无法混合实际类型(或者我不知道如何做到这一点).真正的答案可能需要宏(正如评论中提到的@AlexeyRomanov),但我不知道如何写一个,也许我稍后会提出它.如果您不在这里寻找DI,Still Dynamic可能对您有用

trait Foo {
    def bar(x: Int) = 2 * x
    def baz(y: Int) = 3 * y
  }
  import scala.reflect.runtime.{universe => ru}
  import scala.language.dynamics

  trait Wrapper[T] extends Dynamic {
    val inner: T
    def applyDynamic(name: String)(args: Any*)(implicit tt: ru.TypeTag[T],ct: ClassTag[T]) = {
      val im = tt.mirror.reflect(inner)
      val method = tt.tpe.decl(ru.TermName(name)).asMethod
      println(method)
      val mm = im.reflectMethod(method)
      println(s"$name was called with $args")
      mm.apply(args:_*)
    }
  }

  class W extends Wrapper[Foo] {
    override val inner: Foo = new Foo() {}
  }

  val w = new W // Cannot be casted to Foo
  println(w.bar(5)) // Logs a call and then returns 10

您可以在此处阅读有关Dynamic的更多信息:https://github.com/scala/scala/blob/2.12.x/src/library/scala/Dynamic.scala

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读