Mixin包装Scala特征的每个方法
假设我有几个方法的特性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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 通过SBT运行Scala应用程序时,权限被拒绝绑定到端口
- Memcached source code analysis (threading model)--refer
- Function.const的目的是什么?
- angularjs – ui-router在状态更改之前呈现模板
- angular – ngrx状态未定义
- WebService是“良药”还是“毒药”
- angular-route error对path.json的参数必须是字符串
- MVC5 + EF6 + Bootstrap3 (11) 排序、搜索、分页
- 无法导入自定义组件 – Angular2和TypeScript
- WebSphere 下发布webservice