2

Scala の特性と型消去に問題があります。私はこの特性を持っています:

trait Meta[T] {
  def ~=(e: T): Boolean
}

ここで、パターン マッチングを使用してこのケースを確認します。

(m,i) match {
case (x:Meta[T], y: T) if x ~= y => println ("right")
case _ => println ("wrong")}

Tfromx: Meta[T]は のタイプまたはyサブyタイプである必要がありTます。タイプが一致しない場合は、ClassCastException. ただしx ~= y、型が正しくない場合は実行しないでください。これには回避策がありますか、それとも例外をキャッチしてそのように処理する必要がありますか?

実行例をできるだけ短くしました。

trait Meta[T] {
  type t = T
  def ~=(e: T): Boolean
}

sealed abstract class A 
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
  def ~=(e: A) = e match {
    case e: Ide => true
    case e: MIde => false
  }
}
sealed abstract class B 
case class Foo(s: String) extends B

object Test {

  def m = MIde("x")
  def i = Ide("i")
  def f = Foo("f")

  def main[T](args: Array[String]) {
    (m, i) match {
      case (x: Meta[T], y: T) if x ~= y => println("right")
      case _ => println("wrong")
    }
    // -> right
    (m, f) match {
      case (x: Meta[T], y: T) if x ~= y => println("right")
      case _ => println("wrong")
    }
    // -> Exception in thread "main" java.lang.ClassCastException: 
    // Examples.Foo cannot be cast to Examples.A

  }
}
4

1 に答える 1

0

更新: 最後に代替案を追加しました。

ジェネリック型に関しては、型消去によるパターン マッチングの制限が発生しています。

ただし、すべてが失われるわけではありません。ClassManifests に依存して、クラスをターゲット タイプ T に変換するためのジェネリック メソッドを実装できます (また、Meta[T] に変換する別の同様の方法もあります)。

trait Meta[T] { this: T =>
  type t = T
  def metaManifest: ClassManifest[T]
  def ~=(e: T): Boolean
}

abstract sealed class Base {
  def as[T:ClassManifest]: Option[T] = {
    if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
    else None
  }
  def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
    this match {
      case meta: Meta[_] if classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
      case _ => None
    }
  }
}
abstract sealed class A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
  val metaManifest = classManifest[A]
  def ~=(e: A) = e match {
    case e: Ide => true
    case e: MIde => false
  }
}
sealed abstract class B extends Base
case class Foo(s: String) extends B

これを REPL でテストしてみましょう。

scala> m.as[A]
res17: Option[A] = Some(MIde(x))
scala> m.asMeta[A]
res18: Option[A with Meta[A]] = Some(MIde(x))
scala> i.as[A]
res19: Option[A] = Some(Ide(i))
scala> i.asMeta[A]
res20: Option[A with Meta[A]] = None
scala> f.as[A]
res21: Option[A] = None
scala> f.asMeta[A]
res22: Option[A with Meta[A]] = None

いいですね。これで、パターン マッチングを次のように書き直すことができます。

(m, i) match {
  case (x: Meta[T], y: T) if x ~= y => println("right")
  case _ => println("wrong")
}

これに:

(m.asMeta[T], i.as[T]) match {
  case (Some(x), Some(y)) if x ~= y => println("right")
  case _ => println("wrong")
}

したがって、例は次のようになります。

object Test {

  val m = MIde("x")
  val i = Ide("i")
  val f = Foo("f")

  def test[T:ClassManifest]() {
    (m.asMeta[T], i.as[T]) match {
      case (Some(x), Some(y)) if x ~= y => println("right")
      case _ => println("wrong")
    }
    // -> right
    (m.asMeta[T], f.as[T]) match {
      case (Some(x), Some(y)) if x ~= y => println("right")
      case _ => println("wrong")
    }
  }
}

更新:metaManifest混合するたびに明示的に設定することがオプションでない場合は、 s コンストラクターMetaで暗黙的に渡すことにより、scala に自動的に推論させることができます。Metaこれは、Metaがクラスである必要があり、結果としてAand B(および の型パラメーターとして表示される必要があるすべての同様Metaの型) が特性である必要があることを意味します。これは、2 つのクラスを混在させることができないためです。したがって、基本的に制限を別の制限に交換しています。お好きなものをお選びください。どうぞ:

abstract sealed class Meta[T]( implicit val metaManifest: ClassManifest[T] ) { this: T =>
  type t = T
  def ~=(e: T): Boolean
}

trait Base {
  def as[T:ClassManifest]: Option[T] = {
    if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
    else None
  }
  def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
    this match {
      case meta: Meta[_] if classManifest[T] != ClassManifest.Nothing && classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
      case _ => None
    }
  }
}

trait A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends Meta[A] with A {
  def ~=(e: A) = e match {
    case e: Ide => true
    case e: MIde => false
  }
}
trait B extends Base
case class Foo(s: String) extends B

object Test {
  val m = MIde("x")
  val i = Ide("i")
  val f = Foo("f")

  def test[T:ClassManifest]() {
    (m.asMeta[T], i.as[T]) match {
      case (Some(x), Some(y)) if x ~= y => println("right")
      case _ => println("wrong")
    }
    (m.asMeta[T], f.as[T]) match {
      case (Some(x), Some(y)) if x ~= y => println("right")
      case _ => println("wrong")
    }
  }
}

最後に、どちらの解決策もあなたに合わない場合は、別の解決策を試すことができMeta[T]ますT。のインスタンスをほぼ透過的に のインスタンスのように効果的に使用できるように、Meta[T]ラップされた値にTからの暗黙的な変換を追加することもできます。Meta[T]Meta[T]T

于 2012-10-04T22:49:17.133 に答える