代码之家  ›  专栏  ›  技术社区  ›  Jean-Philippe Pellet

是否可以将“this”作为scala中的隐式参数传递?

  •  9
  • Jean-Philippe Pellet  · 技术社区  · 14 年前

    假设我想用一个记录异常并继续的try-catch块包装可以抛出异常的代码。类似:

    loggingExceptions {
      // something dangerous
    }
    

    理想情况下,我希望使用日志记录在调用对象上定义的记录器,如果有(如果没有,则获取编译时错误)。我想定义如下:

    def loggingExceptions[L <: { def logger: Logger }](work: => Unit)(implicit objectWithLogger: L): Unit = {
      try {
        work
      } catch {
        case t: Exception => objectWithLogger.logger.error(t.getMessage)
      }
    }
    

    其中,ObjectWithLogger将以某种方式在客户端代码中“神奇地”扩展到“this”。这(或类似的事情)可能吗?

    3 回复  |  直到 12 年前
        1
  •  11
  •   psp    14 年前

    事实上,你可以随心所欲地去做。其他回答者投降得太快了。没有白旗!

    package object foo {
      type HasLogger = { def logger: Logger }
      implicit def mkLog(x: HasLogger) = new {
        def loggingExceptions(body: => Unit): Unit =
          try body
          catch { case ex: Exception => println(ex) }
      }
    }
    
    package foo {
      case class Logger(name: String) { }
    
      // Doesn't compile:
      // class A {
      //   def f = this.loggingExceptions(println("hi"))
      // }
      // 1124.scala:14: error: value loggingExceptions is not a member of foo.A
      //         def f = this.loggingExceptions(println("hi"))
      //                      ^
      // one error found  
    
      // Does compile
      class B {
        def logger = Logger("B")
        def f = this.loggingExceptions(println("hi"))
        def g = this.loggingExceptions(throw new Exception)
      }
    }
    
    object Test {
      def main(args: Array[String]): Unit = {
        val b = new foo.B
        b.f
        b.g
      }
    }
    
    // output
    //
    // % scala Test
    // hi
    // java.lang.Exception
    
        2
  •  4
  •   Community Navdeep Singh    7 年前

    Debilski's answer 会有用的,但我不确定是否有充分的理由使用结构类型(即 { def logger: Logger } 在这里。这样做会导致额外的运行时开销 logger 调用,因为结构类型的实现依赖于反射。这个 loggingExceptions 方法与日志记录密切相关,因此我将它作为日志记录特性的一部分:

    trait Logging {
       def logger: Logger
    
       final def loggingExceptions(body: => Unit) =
          try body catch { case e: Exception => logger.error(e.getMessage) }
    }
    
    trait ConcreteLogging extends Logging { 
       val logger = // ...
    }
    
    object MyObject extends SomeClass with ConcreteLogging {
       def main {
          // ...
          loggingExceptions { 
             // ...
          }
       }
    }
    
        3
  •  3
  •   Debilski    14 年前

    你可以为所有想要使用的类添加一个特性 def loggingExceptions 在这个特性中,添加一个期望 def logger: Logger 随时可用。

    trait LoggingExceptions {
        this: { def logger: Logger } =>
      def loggingExceptions(work: => Unit) {
        try { work }
        catch { case t: Exception => logger.error(t.getMessage) }
      }
    }
    
    object MyObjectWithLogging extends OtherClass with LoggingExceptions {
      def logger: Logger = // ...
    
      def main {
        // ...
        loggingExceptions { // ...
        }
      }
    }