2010-11-24 80 views
8

假設我想打包可以用一個try-catch塊來拋出異常的代碼,該塊記錄異常並繼續。喜歡的東西:是否有可能將「this」作爲隱式參數傳遞給Scala?

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會以某種方式「神奇」擴展爲「本」,在客戶端代碼。這是(或類似的東西)嗎?

回答

11

它其實可以做,就像你想要的。其他答覆者投降得太快。沒有白旗!

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 
3

你可以添加一個trait到所有想使用def loggingExceptions的類,並在這個trait中添加一個自我類型,這個自我類型需要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 { // ... 
    } 
    } 
} 
+0

謝謝,這個作品!但有沒有其他解決方案不涉及改變所有希望使用loggingExceptions(...)的類的聲明? – 2010-11-24 19:23:55

+0

@JPP不,至少該調用站點需要在範圍內具有預期類型的​​隱式對象。例如,您可以將隱式參數設置爲Logger,並在調用對象中將def logger更改爲implicit def logger。然而,除非必要,否則應該避免暗示,並且特徵非常適合這個問題。示例中的 – 2010-11-24 19:47:25

4

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 { 
     // ... 
     } 
    } 
} 
相關問題