Обновление Это макрос. Это было гораздо менее болезненно, чем я думал, что это будет из-за квазиквартир. Они замечательные. Этот код делает только немного, и вам, вероятно, придется его улучшить. Он может не учитывать некоторые особые ситуации. Также предполагается, что ни у родительского класса, ни у его метода нет параметров типа, он обертывает только методы данного класса или признака, но не методы его родителей, он может не работать, если у вас есть вспомогательные конструкторы и т. Д. Тем не менее, я надеюсь, что это даст вам идея о том, как сделать это для ваших конкретных потребностей, заставляя ее работать во всех ситуациях, к сожалению, слишком большая работа для меня прямо сейчас.
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 предложил в комментариях), но я не уверен, как написать один, возможно, я придумаю это позже. Тем не менее Dynamic
может работать для вас, если вы не ищете DI здесь
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
Это должно быть выполнимо с помощью макросов аннотаций (HTTP: //docs.scala-lang .org/overviews/macro/annotations), я думаю. Это комментарий, а не ответ, потому что я не могу написать макрос на данный момент. –
Вы также можете использовать шаблон стекируемых признаков, чтобы добавить определенное поведение к существующим признакам, так что ваши вызовы выглядят так: val a = new SomeClass with PrintCall с Logger with Whatever. Вы просто вводите поведение в том порядке, в котором хотите, чтобы они выполнялись (в случае печати и регистрации это не имеет значения, но если ваши черты - это AddTwo и MultiplyByThree, то порядок имеет значение). Я пишу в комментарии, потому что это не то, о чем вы просили, но если вы не знакомы с этим шаблоном, проверьте это, это может быть довольно круто (однако вам нужно явно переопределить каждый метод). – slouc
@slouc Я надеюсь на решение, которое не означает, что я должен написать 'override def bar' вручную для каждого метода. В основном, поскольку это означает, что я не могу написать это в общем виде (не зная заранее всех методов и подписей). –