Scalaケースクラスマッチングコンパイルエラーとエイリアスの内部型?

StackOverflow https://stackoverflow.com/questions/1812695

  •  06-07-2019
  •  | 
  •  

質問

エイリアスタイプでのケースクラスマッチングの使用方法これは、コンテナなどから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
}

ありがとう!

役に立ちましたか?

解決

右... 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 および b の最も一般的なタイプは Foo#Bar です。これは、内部クラス Bar を意味します Foo タイプの外部オブジェクトを使用します。ただし、 a のタイプは x.Bar であり、 b のタイプは y.Bar -つまり、 a は、 bと同様に、外側のオブジェクト x を持つ内部クラス Bar のインスタンスです。

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

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

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

では、コードスニペットに戻りましょう。パターンマッチングを行う場合、実際にはコンストラクターを呼び出します- 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.CB 型であることを保証できません。 c d の場合のCB はたまたま同じです。

この例を検討してください:

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

where x および y は以前と同じです。以下を実行してみてください:

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

希望:)

-フラビウシプシガン

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top