2010-11-24 8 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 
+2

+1です。これはできますが、それはお勧めできません。ロギング特性とロギング特性を分離せず、「暗黙のマジック」を必要とし、不要なコンパイル時(暗黙の検索)と実行時(リフレクション)のオーバーヘッドを招きます。実装では、暗黙の変換が有効である必要があるという事実が隠されており、より現実的なコードベースでは、クライアントコードにimport文が必要になります。 –

+4

あなたのネガのリストは「私はむしろ別の問題を解決したい」と要約しています。まあ、ええ、私たち全員ではありません。 – extempore

+0

実際にはありません。基本的な問題は、ロガーを持つクラス内のコードブロックに例外を記録することです。この問題は、当初OPが意図していた方法(黙示的に)で解決することができます。あるいは、それをより簡単に特性で解決することができます。 –

3

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 { // ... 
    } 
    } 
} 
+0

ありがとうございます!しかし、loggingExceptions(...)を使用するすべてのクラスの宣言を変更することを伴わない他の解決策がありますか? –

+0

@ JPPいいえ、少なくとも、コールサイトはスコープ内に予想される型の暗黙のオブジェクトを持つ必要があります。たとえば、暗黙的なパラメータを 'Logger'にすることができ、' def logger'を呼び出しオブジェクトの '暗黙のdef logger'に変更することができます。しかし、必要がない限り、インプリカイトは避けるべきであり、形質がこの問題に適している。例の場合は –

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 { 
     // ... 
     } 
    } 
} 
関連する問題