37

Javaの例外には、少なくとも次の4つのコンストラクタがあります。

Exception() 
Exception(String message) 
Exception(String message, Throwable cause) 
Exception(Throwable cause) 

独自の拡張機能を定義する場合は、子孫の例外を宣言し、対応するスーパーコンストラクターを呼び出して必要な各コンストラクターを実装する必要があります。

どうすればscalaで同じことを達成できますか?

これまでのところ、私はこの記事とこのSOの答えを見ましたが、そのような一般的なことを達成するためのより簡単な方法があるに違いないと思います

4

6 に答える 6

58

のデフォルト値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
于 2012-06-07T03:46:07.367 に答える
12

さて、これは私がこれまでに見つけた中で最高です

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)
}

このようにして、「newMissingConfigurationException」またはコンパニオンオブジェクトのapplyメソッドを使用できます。

とにかく、それを達成するためのより簡単な方法がないことに私はまだ驚いています

于 2012-06-07T03:46:02.523 に答える
6

を使用できますThrowable.initCause

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

    def this(message: String) = this(message, null)  
}
于 2012-08-02T13:32:49.243 に答える
5

私には、互いに動的な緊張関係がある3つの異なるニーズがあるように見えます。

  1. のエクステンダーの便利さRuntimeException; つまり、の子孫を作成するために記述される最小限のコードRuntimeException
  2. クライアントの知覚された使いやすさ。つまり、コールサイトで記述される最小限のコード
  3. null恐ろしいJavaをコードに漏らさないようにするというクライアントの好み

3番を気にしないのであれば、この答え(これと同じ)はかなり簡潔に思えます。

ただし、1と2にできるだけ近づけようとしているときに、1つの値が3である場合、以下のソリューションは、JavanullリークをScalaAPIに効果的にカプセル化します。

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)
}

newまた、実際に使用されている場所を使用する必要をなくしたい場合はMyRuntimeException、このコンパニオンオブジェクトを追加します(これにより、すべての適用呼び出しが既存の「マスター」クラスコンストラクターに転送されます)。

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将来のリファクタリングを容易にするために、できるだけ多くのコードで演算子の使用を実際に抑制したいと思っています。上記のリファクタリングがファクトリパターンに強く影響する場合は特に役立ちます。私の最終的な結果は、より冗長ですが、クライアントが使用するには非常に良いはずです。

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
)


より洗練されたRuntimeExceptionパターンの調査:

RuntimeExceptionパッケージまたはAPIに特化したエコシステムを作成したいというのは、元の質問からのほんの小さな飛躍です。RuntimeExceptionアイデアは、特定の子孫例外の新しいエコシステムを作成できる「ルート」を定義することです。catch私にとっては、特定の種類のエラーを使用し、match悪用しやすくすることが重要でした。

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

RuntimeExceptionこの特定の問題空間には、1つの抽象(FailedPrecondition)と2つの具象(FailedPreconditionMustBeNonEmptyListおよび)の3つの異なる子孫がありFailedPreconditionsExceptionます。

最初のFailedPrecondition、は、の直接の子孫でありRuntimeException、に非常に似てMyRuntimeExceptionおり、抽象的です(直接のインスタンス化を防ぐため)。インスタンス化ファクトリとして機能する(オペレータを抑制する) FailedPrecondition「コンパニオンオブジェクト特性」があります。FailedPreconditionObjectnew

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
)

2番目の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
  )

3番目の、は、sのaをラップし、例外メッセージの発行を動的に管理するFailedPreconditionsException直接の子孫です。RuntimeExceptionListFailedPrecondition

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))

そして、それらすべてを全体としてまとめて整理し、両方FailedPreconditionFailedPreconditionMustBeNonEmptyListオブジェクト内に配置しますFailedPreconditionsException。そして、これが最終結果のようになります。

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))

そして、これは、クライアントが上記のコードを使用して、次のような独自の例外派生を作成する場合にどのように見えるかを示していますFailedPreconditionMustBeNonEmptyString

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()

私は元の質問に答えるだけでなく、Scalaで具体的かつ包括的であることに近いものを見つけるのが非常に難しいことを発見しましたRuntimeException。具体的には、Javaにいるときにとても快適になったより一般的な「例外エコシステム」に拡張します。

ソリューションセットに関するフィードバック(「うわー!それは私には冗長すぎる」のバリエーションを除く)を聞きたいです。そして、このパターンのクライアントのために生成した値や簡潔さを失うことなく、冗長性を減らすための追加の最適化や方法があればいいのですが。

于 2015-10-23T22:25:28.077 に答える
0

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

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」が出力されます。

于 2016-11-12T06:37:09.247 に答える
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)) 
于 2016-12-18T15:36:36.370 に答える