Допустим, у вас уже есть базовый класс для всех логгеров:
abstract class Logger {
def info(msg:String):Unit
}
Тогда вы можете расширить String с помощью метода ведения журнала @@
:
object ExpressionLog {
// default logger
implicit val logger = new Logger {
def info(s:String) {println(s)}
}
// adding @@ method to all String objects
implicit def stringToLog (msg: String) (implicit logger: Logger) = new {
def @@ [T] (exp: T) = {
logger.info(msg + " = " + exp)
exp
}
}
}
Чтобы использовать ведение журнала, вам необходимо импортировать элементы объекта ExpressionLog
, а затем вы можете легко записывать выражения, используя следующую запись:
import ExpressionLog._
def sum (a:Int, b:Int) = "sum result" @@ (a+b)
val c = sum("a" @@ 1, "b" @@2)
Напечатает:
a = 1
b = 2
sum result = 3
Это работает, потому что каждый раз, когда вы вызываете метод @@
на компиляторе String
, вы понимаете, что String
не имеет метода, и автоматически преобразует его в объект с анонимным типом, который имеет метод @@
определены (см. stringToLog
). Как часть компилятора преобразования выбирает нужный регистратор как неявный параметр , таким образом вам не нужно постоянно передавать регистратор на @@
каждый раз, пока вы сохраняете полный контроль над тем, какие потребности регистратора использоваться каждый раз.
Что касается приоритета, когда метод @@
используется в инфиксной нотации, он имеет наивысший приоритет , что упрощает рассуждение о том, что будет регистрироваться.
Так что, если вы хотите использовать другой регистратор в одном из ваших методов? Это очень просто:
import ExpressionLog.{logger=>_,_} // import everything but default logger
// define specific local logger
// this can be as simple as: implicit val logger = new MyLogger
implicit val logger = new Logger {
var lineno = 1
def info(s:String) {
println("%03d".format(lineno) + ": " + s)
lineno+=1
}
}
// start logging
def sum (a:Int, b:Int) = a+b
val c = "sum result" @@ sum("a" @@ 1, "b" @@2)
Будет выводить:
001: a = 1
002: b = 2
003: sum result = 3