2012-06-07 13 views
34

にオーバーロードされたコンストラクタを使用して独自の例外を定義し、少なくともこれら四つのコンストラクタを持っています対応するスーパーコンストラクタを呼び出すコンストラクタJava例外ではScalaの

どのようにスカラで同じことを達成できますか?

は、これまでのところ、今、私はthis articleと、このSO answerを見ましたが、私は

答えて

56

causeのデフォルト値はnullです。そしてmessageことがcause.toString()またはnullのいずれかです:

val e1 = new RuntimeException() 

e.getCause 
// res1: java.lang.Throwable = null 

e.getMessage 
//res2: java.lang.String = null 

val cause = new RuntimeException("cause msg") 
val e2 = new RuntimeException(cause) 

e.getMessage() 
//res3: String = java.lang.RuntimeException: cause msg 

だから、あなただけのデフォルト値を使用することができます。

class MyException(message: String = null, cause: Throwable = null) extends 
    RuntimeException(MyException.defaultMessage(message, cause), cause) 

object MyException { 
    def defaultMessage(message: String, cause: Throwable) = 
    if (message != null) message 
    else if (cause != null) cause.toString() 
    else null 
} 

// usage: 
new MyException(cause = myCause) 
// res0: MyException = MyException: java.lang.RuntimeException: myCause msg 
+0

は例外がのRuntimeExceptionを拡張するべきではないのですか? – opensas

+0

さらに、ケースクラスとして宣言すれば、 "new"を取り除くことができます... – opensas

+1

@opensas 'object MyException {def apply(message:String = null、cause:Throwable = null)= new MyException(メッセージ、原因)} 'で十分です。 – senia

11

だけでなく、このような共通点を達成するための簡単な方法がなければならない疑いがある、これは私が見つけた最高です、これまで

class MissingConfigurationException private(ex: RuntimeException) extends RuntimeException(ex) { 
    def this(message:String) = this(new RuntimeException(message)) 
    def this(message:String, throwable: Throwable) = this(new RuntimeException(message, throwable)) 
} 

object MissingConfigurationException { 
    def apply(message:String) = new MissingConfigurationException(message) 
    def apply(message:String, throwable: Throwable) = new MissingConfigurationException(message, throwable) 
} 

あなたは「新しいMissingConfigurationException」を使用するか、とにかくコンパニオンオブジェクト

から方法を適用することができるこの方法は、私はまだそれ

を達成するための簡単な方法がないことを驚いています
6

あなたはThrowable.initCauseを使用することができます。私に

class MyException (message: String, cause: Throwable) 
    extends RuntimeException(message) { 
    if (cause != null) 
     initCause(cause) 

    def this(message: String) = this(message, null) 
} 
5

は、相互に動的なテンションを持っている三つの異なるニーズがあります表示されます。

  1. RuntimeExceptionのエクステンダーの利便性。すなわち、子孫を作成するために書かれるべき最小限のコードRuntimeException
  2. クライアントの知覚された使い易さ;つまり、最小限のコードは、呼び出しサイトで書き込まれる
  3. 1はこの1つに、その後、数3について this answer(ピアを気にしない場合は、そのコード

に恐ろしいのJava null漏れ回避する

  • クライアントの好み)かなり簡潔なようです。

    ただし、1と2にできるだけ近づけようとすると数値が3になる場合、以下のソリューションはJava nullをScala APIに効果的にカプセル化します。

    class MyRuntimeException (
        val optionMessage: Option[String], 
        val optionCause: Option[Throwable], 
        val isEnableSuppression: Boolean, 
        val isWritableStackTrace: Boolean 
    ) extends RuntimeException(
        optionMessage match { 
        case Some(string) => string 
        case None => null 
        }, 
        optionCause match { 
        case Some(throwable) => throwable 
        case None => null 
        }, 
        isEnableSuppression, 
        isWritableStackTrace 
    ) { 
        def this() = 
        this(None, None, false, false) 
        def this(message: String) = 
        this(Some(message), None, false, false) 
        def this(cause: Throwable) = 
        this(None, Some(cause), false, false) 
        def this(message: String, cause: Throwable) = 
        this(Some(message), Some(cause), false, false) 
    } 
    

    そして、あなたはMyRuntimeExceptionが実際に使用されnewを使用する必要が排除したい場合は、このコンパニオンオブジェクトを追加します(これは単に転送既存の「マスター」クラスのコンストラクタへの適用のコールのすべて):

    object MyRuntimeException { 
        def apply: MyRuntimeException = 
        MyRuntimeException() 
        def apply(message: String): MyRuntimeException = 
        MyRuntimeException(optionMessage = Some(message)) 
        def apply(cause: Throwable): MyRuntimeException = 
        MyRuntimeException(optionCause = Some(cause)) 
        def apply(message: String, cause: Throwable): MyRuntimeException = 
        MyRuntimeException(optionMessage = Some(message), optionCause = Some(cause)) 
        def apply(
        optionMessage: Option[String] = None, 
        optionCause: Option[Throwable] = None, 
        isEnableSuppression: Boolean = false, 
        isWritableStackTrace: Boolean = false 
    ): MyRuntimeException = 
        new MyRuntimeException(
         optionMessage, 
         optionCause, 
         isEnableSuppression, 
         isWritableStackTrace 
        ) 
    } 
    

    個人的には、可能な限り多くのコードでnew演算子の使用を実際に抑制し、将来のリファクタリングを容易にすることを好みます。リファクタリングがFactoryパターンに強く起こった場合は、特に役立ちます。私の最終的な結果は、より冗長ではあるが、クライアントが使用するのにはすばらしいものでなければならない。より洗練されたのRuntimeExceptionパターン探る

    object MyRuntimeException { 
        def apply: MyRuntimeException = 
        MyRuntimeException() 
        def apply(message: String): MyRuntimeException = 
        MyRuntimeException(optionMessage = Some(message)) 
        def apply(cause: Throwable): MyRuntimeException = 
        MyRuntimeException(optionCause = Some(cause)) 
        def apply(message: String, cause: Throwable): MyRuntimeException = 
        MyRuntimeException(optionMessage = Some(message), optionCause = Some(cause)) 
        def apply(
        optionMessage: Option[String] = None, 
        optionCause: Option[Throwable] = None, 
        isEnableSuppression: Boolean = false, 
        isWritableStackTrace: Boolean = false 
    ): MyRuntimeException = 
        new MyRuntimeException(
         optionMessage, 
         optionCause, 
         isEnableSuppression, 
         isWritableStackTrace 
        ) 
    } 
    
    class MyRuntimeException private[MyRuntimeException] (
        val optionMessage: Option[String], 
        val optionCause: Option[Throwable], 
        val isEnableSuppression: Boolean, 
        val isWritableStackTrace: Boolean 
    ) extends RuntimeException(
        optionMessage match { 
        case Some(string) => string 
        case None => null 
        }, 
        optionCause match { 
        case Some(throwable) => throwable 
        case None => null 
        }, 
        isEnableSuppression, 
        isWritableStackTrace 
    ) 
    


    をそれは、パッケージやAPIに特化RuntimeException秒の生態系を作成することを望むために元の質問からわずかな飛躍です。このアイデアは、特定の子孫例外の新しいエコシステムを作成できる「ルート」RuntimeExceptionを定義することです。私にとっては、catchmatchを使って特定の種類のエラーを悪用するほうがはるかに簡単です。

    例えば、ケースクラスの作成を許可する前に一連の条件を検証するvalidateメソッドが定義されています。失敗した各条件は、RuntimeExceptionインスタンスを生成します。そして、RuntimeInstanceのListがメソッドによって返されます。これにより、クライアントは応答をどのように処理するかを決めることができます。 throwリスト保留例外では、特定のものをリストでスキャンしてthrowをスキャンするか、非常に高価なJVM throwコマンドを使用せずに、コールチェーン全体をプッシュするだけです。

    この特定の問題空間はRuntimeException、1つの抽象(FailedPrecondition)及び(FailedPreconditionMustBeNonEmptyListFailedPreconditionsException)2コンクリートの三つの異なる子孫を持っています。

    まず、FailedPreconditionは、MyRuntimeExceptionに非常に類似しRuntimeExceptionへの直接の子孫であり、および(直接インスタンス化を防止する)抽象的です。 FailedPreconditionには、インスタンス化ファクトリ(new演算子を抑制する)として機能する「コンパニオンオブジェクト特性」FailedPreconditionObjectがあります。

    trait FailedPreconditionObject[F <: FailedPrecondition] { 
        def apply: F = 
        apply() 
    
        def apply(message: String): F = 
        apply(optionMessage = Some(message)) 
    
        def apply(cause: Throwable): F = 
        apply(optionCause = Some(cause)) 
    
        def apply(message: String, cause: Throwable): F = 
        apply(optionMessage = Some(message), optionCause = Some(cause)) 
    
        def apply(
         optionMessage: Option[String] = None 
        , optionCause: Option[Throwable] = None 
        , isEnableSuppression: Boolean = false 
        , isWritableStackTrace: Boolean = false 
    ): F 
    } 
    abstract class FailedPrecondition (
        val optionMessage: Option[String], 
        val optionCause: Option[Throwable], 
        val isEnableSuppression: Boolean, 
        val isWritableStackTrace: Boolean 
    ) extends RuntimeException(
        optionMessage match { 
        case Some(string) => string 
        case None => null 
        }, 
        optionCause match { 
        case Some(throwable) => throwable 
        case None => null 
        }, 
        isEnableSuppression, 
        isWritableStackTrace 
    ) 
    

    秒、FailedPreconditionMustBeNonEmptyList、間接RuntimeException子孫とFailedPreconditionの直接具体的な実装です。これは、コンパニオンオブジェクトとクラスの両方を定義します。コンパニオンオブジェクトは、特性FailedPreconditionObjectを拡張します。また、このクラスでは抽象クラスFailedPreconditionを拡張し、それ以上の拡張を防ぐためにfinalとマークします。

    object FailedPreconditionMustBeNonEmptyList extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyList] { 
        def apply(
         optionMessage: Option[String] = None 
        , optionCause: Option[Throwable] = None 
        , isEnableSuppression: Boolean = false 
        , isWritableStackTrace: Boolean = false 
    ): FailedPreconditionMustBeNonEmptyList = 
        new FailedPreconditionMustBeNonEmptyList(
         optionMessage 
         , optionCause 
         , isEnableSuppression 
         , isWritableStackTrace 
        ) 
    } 
    final class FailedPreconditionMustBeNonEmptyList private[FailedPreconditionMustBeNonEmptyList] (
        optionMessage: Option[String] 
        , optionCause: Option[Throwable] 
        , isEnableSuppression: Boolean 
        , isWritableStackTrace: Boolean 
    ) extends 
        FailedPrecondition(
         optionMessage 
        , optionCause 
        , isEnableSuppression 
        , isWritableStackTrace 
    ) 
    

    第三、FailedPreconditionsException、例外メッセージの発光を管理する動的次いでListFailedPreconditionのSをラップしRuntimeExceptionへの直接の子孫です。全体できちんと物事アップとして一緒にそのすべてを持って来る

    object FailedPreconditionsException { 
        def apply(failedPrecondition: FailedPrecondition): FailedPreconditionsException = 
        FailedPreconditionsException(List(failedPrecondition)) 
        def apply(failedPreconditions: List[FailedPrecondition]): FailedPreconditionsException = 
        tryApply(failedPreconditions).get 
        def tryApply(failedPrecondition: FailedPrecondition): Try[FailedPreconditionsException] = 
        tryApply(List(failedPrecondition)) 
        def tryApply(failedPreconditions: List[FailedPrecondition]): Try[FailedPreconditionsException] = 
        if (failedPreconditions.nonEmpty) 
         Success(new FailedPreconditionsException(failedPreconditions)) 
        else 
         Failure(FailedPreconditionMustBeNonEmptyList()) 
        private def composeMessage(failedPreconditions: List[FailedPrecondition]): String = 
        if (failedPreconditions.size > 1) 
         s"failed preconditions [${failedPreconditions.size}] have occurred - ${failedPreconditions.map(_.optionMessage.getOrElse("")).mkString("|")}" 
        else 
         s"failed precondition has occurred - ${failedPreconditions.head.optionMessage.getOrElse("")}" 
    } 
    final class FailedPreconditionsException private[FailedPreconditionsException] (
        val failedPreconditions: List[FailedPrecondition] 
    ) extends RuntimeException(FailedPreconditionsException.composeMessage(failedPreconditions)) 
    

    そして、私はオブジェクトFailedPreconditionsExceptionFailedPreconditionFailedPreconditionMustBeNonEmptyListの両方を配置します。そして、これは、最終的な結果は次のようになります。FailedPreconditionMustBeNonEmptyStringと呼ば

    object FailedPreconditionsException { 
        trait FailedPreconditionObject[F <: FailedPrecondition] { 
        def apply: F = 
         apply() 
    
        def apply(message: String): F = 
         apply(optionMessage = Some(message)) 
    
        def apply(cause: Throwable): F = 
         apply(optionCause = Some(cause)) 
    
        def apply(message: String, cause: Throwable): F = 
         apply(optionMessage = Some(message), optionCause = Some(cause)) 
    
        def apply(
         optionMessage: Option[String] = None 
         , optionCause: Option[Throwable] = None 
         , isEnableSuppression: Boolean = false 
         , isWritableStackTrace: Boolean = false 
        ): F 
        } 
        abstract class FailedPrecondition (
         val optionMessage: Option[String] 
        , val optionCause: Option[Throwable] 
        , val isEnableSuppression: Boolean 
        , val isWritableStackTrace: Boolean 
    ) extends RuntimeException(
        optionMessage match { 
         case Some(string) => string 
         case None => null 
        }, 
        optionCause match { 
         case Some(throwable) => throwable 
         case None => null 
        }, 
        isEnableSuppression, 
        isWritableStackTrace 
    ) 
    
        object FailedPreconditionMustBeNonEmptyList extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyList] { 
        def apply(
         optionMessage: Option[String] = None 
         , optionCause: Option[Throwable] = None 
         , isEnableSuppression: Boolean = false 
         , isWritableStackTrace: Boolean = false 
        ): FailedPreconditionMustBeNonEmptyList = 
         new FailedPreconditionMustBeNonEmptyList(
          optionMessage 
         , optionCause 
         , isEnableSuppression 
         , isWritableStackTrace 
        ) 
        } 
        final class FailedPreconditionMustBeNonEmptyList private[FailedPreconditionMustBeNonEmptyList] (
         optionMessage: Option[String] 
        , optionCause: Option[Throwable] 
        , isEnableSuppression: Boolean 
        , isWritableStackTrace: Boolean 
    ) extends 
        FailedPrecondition(
         optionMessage 
         , optionCause 
         , isEnableSuppression 
         , isWritableStackTrace 
        ) 
    
        def apply(failedPrecondition: FailedPrecondition): FailedPreconditionsException = 
        FailedPreconditionsException(List(failedPrecondition)) 
    
        def apply(failedPreconditions: List[FailedPrecondition]): FailedPreconditionsException = 
        tryApply(failedPreconditions).get 
    
        def tryApply(failedPrecondition: FailedPrecondition): Try[FailedPreconditionsException] = 
        tryApply(List(failedPrecondition)) 
    
        def tryApply(failedPreconditions: List[FailedPrecondition]): Try[FailedPreconditionsException] = 
        if (failedPreconditions.nonEmpty) 
         Success(new FailedPreconditionsException(failedPreconditions)) 
        else 
         Failure(FailedPreconditionMustBeNonEmptyList()) 
        private def composeMessage(failedPreconditions: List[FailedPrecondition]): String = 
        if (failedPreconditions.size > 1) 
         s"failed preconditions [${failedPreconditions.size}] have occurred - ${failedPreconditions.map(_.optionMessage.getOrElse("")).mkString("|")}" 
        else 
         s"failed precondition has occurred - ${failedPreconditions.head.optionMessage.getOrElse("")}" 
    } 
    final class FailedPreconditionsException private[FailedPreconditionsException] (
        val failedPreconditions: List[FailedPreconditionsException.FailedPrecondition] 
    ) extends RuntimeException(FailedPreconditionsException.composeMessage(failedPreconditions)) 
    

    そして、これは、それが自分の例外導出を作成するために、上記のコードを使用するようにクライアントのためにどのように見えるかです:

    object FailedPreconditionMustBeNonEmptyString extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyString] { 
        def apply(
         optionMessage: Option[String] = None 
        , optionCause: Option[Throwable] = None 
        , isEnableSuppression: Boolean = false 
        , isWritableStackTrace: Boolean = false 
    ): FailedPreconditionMustBeNonEmptyString = 
        new FailedPreconditionMustBeNonEmptyString(
         optionMessage 
         , optionCause 
         , isEnableSuppression 
         , isWritableStackTrace 
        ) 
    } 
    final class FailedPreconditionMustBeNonEmptyString private[FailedPreconditionMustBeNonEmptyString] (
        optionMessage: Option[String] 
        , optionCause: Option[Throwable] 
        , isEnableSuppression: Boolean 
        , isWritableStackTrace: Boolean 
    ) extends 
        FailedPrecondition(
         optionMessage 
        , optionCause 
        , isEnableSuppression 
        , isWritableStackTrace 
    ) 
    

    とその後、この例外の使用は次のようになります。

    throw FailedPreconditionMustBeNonEmptyString() 
    
    私はそれはとても難しいanythを見つけたので、私は元の質問に答えるを超えてうまくいった

    具体的にはRuntimeExceptionでScala-ifyingで具体的かつ包括的であることに近づくか、より一般的な「例外エコシステム」に至るまで、Javaで快適になった。

    私の解決策のセットでは、フィードバックをお聞きしたいと思います(「Wow!これは私にとってはあまりにも冗長です」)。そして私は、このパターンのクライアントのために生成した値や簡潔さを失うことなく、冗長性を減らすための追加の最適化や方法を愛しています。

  • 0

    try/catchブロック内のスカラパターンマッチングは、インターフェイスで機能します。私の解決策は、例外名のインターフェイスを使用し、別のクラスインスタンスを使用することです。

    trait MyException extends RuntimeException 
    
    class MyExceptionEmpty() extends RuntimeException with MyException 
    
    class MyExceptionStr(msg: String) extends RuntimeException(msg) with MyException 
    
    class MyExceptionEx(t: Throwable) extends RuntimeException(t) with MyException 
    
    object MyException { 
        def apply(): MyException = new MyExceptionEmpty() 
        def apply(msg: String): MyException = new MyExceptionStr(msg) 
        def apply(t: Throwable): MyException = new MyExceptionEx(t) 
    } 
    
    class MyClass { 
        try { 
        throw MyException("oops") 
        } catch { 
        case e: MyException => println(e.getMessage) 
        case _: Throwable => println("nope") 
        } 
    } 
    

    「MyClass」をインスタンス化すると「oops」が出力されます。

    0

    @ roman-borisovに類似したアプローチですが、より型保証されています。

    case class ShortException(message: String = "", cause: Option[Throwable] = None) 
        extends Exception(message) { 
        cause.foreach(initCause) 
    } 
    

    その後、あなたは、Java的に例外を作成することができます。

    throw ShortException() 
    throw ShortException(message) 
    throw ShortException(message, Some(cause)) 
    throw ShortException(cause = Some(cause))