4

エイリアスタイプとのケースクラスマッチングを使用するにはどうすればよいですか?これは、CBなどをコンテナから引き出すときに機能します。

class DoStuff[TKey](
  val c : Container[TKey]#CB
)
{
  type CB = Container[TKey]#CB
  type C1 = Container[TKey]#C1
  type C2 = Container[TKey]#C2

  c match {
    case C1(e1) => e1  //   - not found: value e1   - not found: value C1
    case C2(e2) => e2 //    - not found: value e2   - not found: value C2
  }
}

trait Container[TKey]
{
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
}

ありがとう!

4

1 に答える 1

8

そうです...Scalaの内部クラスは少し厄介です。提供したコードの書き直されたバージョンを示す前に、簡単な例を試してみましょう。

case class Foo(x: Int) {
  case class Bar(y: String) 
}

ここで、次のコードスニペットについて考えてみます。

val x = new Foo(1)
val y = new Foo(2)

val a = new x.Bar("one")
val b = new y.Bar("two") 

との最も一般的なタイプはです。これはa、タイプが任意の外部オブジェクトを持つ内部クラスを意味します。しかし、isの型とisの型は、より具体的に言うことができます。これは、の場合と同様に、外部オブジェクトを持つ内部クラスのインスタンスであることを意味します。bFoo#BarBarFooax.Barby.BaraBarxb

typeOf(a)とを呼び出すと、タイプが異なることが実際にわかりますtypeOf(b)。ここで、typeOfはそのように定義されたユーティリティメソッドです。(それは非常に素晴らしい型推論とManifestsの少しの使用によってその引数の型を与えるだけです)

def typeOf[T](x: T)(implicit m: scala.reflect.Manifest[T]) = m.toString

内部オブジェクトはそれを囲むオブジェクトへの参照を保持しているため、外部オブジェクトを何らかの方法で指定せずに内部オブジェクトをインスタンス化することはできません。したがって、呼び出すことはできますnew x.Bar("one")が、呼び出すことはできませんnew Foo#Bar("?")。2番目のケースでは、作成しようとしている新しいオブジェクトの内部オブジェクトを指定していません。

それでは、コードスニペットに戻りましょう。パターンマッチングを行う場合、実際にはコンストラクターを呼び出します-を呼び出す場合C1(e1)C1エイリアスであるようContainer[TKey]#C1 に、外部オブジェクトを指定せずに内部クラスのコンストラクターを呼び出そうとしましたが、上記の理由により失敗します。私がコードを書く方法は次のようになります:

trait Container[TKey] {
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
}

class DoStuff[TKey] (val c: Container[TKey], val element: Container[TKey]#CB) {
  element match {
    case c.C1(e1) => Some(e1)
    case c.C2(e2) => Some(e2)
    case _        => None
  }
}

これでコンパイルされ、うまくいけばあなたが望むことをすることができます。しかし、これには細心の注意を払ってください!型消去のため、Scalaは、elementが実際に型であるc.CBか、または場合によっては同じであるかを保証できません。d.CBCBcd

この例を考えてみましょう。

def matcher(arg: Foo#Bar) = {
  arg match {
    case x.Bar(n) => println("x");
    case y.Bar(n) => println("y");
  }
}

ここでxy以前と同じです。次のコマンドを実行してみてください。

matcher(a)
matcher(b) 

どちらも印刷しますx

したがって、コンテナに要素を明示的に含めるようにコードを書き直します。

trait Container[TKey] {
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
    val element: CB
}

class DoStuff[TKey](val c: Container[TKey]) {
  c.element match {
    case c.C1(e1) => Some(e1)
    case c.C2(e2) => Some(e2)
    case _        => None
  }
}

それが役に立てば幸い :)

--Flaviu Cipcigan

于 2009-11-28T19:32:27.013 に答える