2017-01-24 7 views
4

をパラメータとして[BELOW EDITED]派生クラスクラスのコンストラクタは

私は、おおまかに以下の、今、あるクラス階層を持っている:

sealed trait Foo { 
    def method: Any 
} 

case class Bar extends Foo { 
    def method: Array[String] = // implementation 
} 

case class Baz extends Foo { 
    def method: Map[String, Array[String]] = // implementation 
} 

私は抽象メソッドにAnyの戻り値の型を与えていますなぜなら、ケースクラスの戻り値の型は必然的に異なるからですが、それらは同じ目的を共有しているからです。このような理由から、私はその共通の振る舞いをモデル化するためにそれを特性に残しておきたいと思います。これがコンパイルされた唯一の方法です。これがScalaの型システムの精神に反していることを認識しているので、私は以下の最初の質問をします。

その後、別のクラスは、コンストラクタのパラメータとしてFooのサブクラスを期待して、私は次の以外、これを意味するのか分からない:

class Qux(foo: Foo) { 
    val m = foo.method 
    ... 
    ... 
} 

後半クラスQuxで期待する方法がありますヴァルmFooの特定のサブクラス(BarまたはBaz)に対応するタイプのものであると、私は

... type mismatch; 
[error] found : Any 
[error] required: Array[String] 
のようなコンパイルエラーを取得しています

  • 私はこれが私の特定の問題を表現するための正しい方法であると信じてするのに十分なスカラ座に精通していますが、それについて移動する方法を知っているのに十分それに慣れていない:

    は、だから私は、カップルの質問があります。私がやろうとしていることをする正しい方法は何ですか?

  • また、mBarまたはBazから特定のmethodによって返された値、およびないFooから抽象メソッドとして扱われるべきであるというクラスQuxを伝える方法はありますか?

編集:は(抽象型メンバーを使用して)@mariosによって提案されたアプローチを取るには、正しい方向への第一歩であると考えられるが、型の不一致は今ポップアップ表示されます。クラスQuxの中で、私は今

class Qux[X <: Foo](sc: SparkContext, val foo: X) { 
    val m: foo.A = foo.method 

    def process(rows: DataFrame) = foo match { 
    case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows) 
    case Baz(sc, _) => BazProcessor(sc, m.keys).doStuff(rows, m.values) 
    } 
} 

を有する場合BarProcessorは、例えば、でインスタンスArray[String]、およびBazProcessorは、ものを行うためにBazmethodによって返された値からキーと値のペアを必要としています。私はfooは(value keys is not a member of Qux.this.foo.Aの線に沿って、など)BazときmMap固有のメソッドを呼び出すしようとすると、しかし、私は今

[error] Qux.scala:4: type mismatch; 
[error] found : Qux.this.foo.A 
[error] required: Array[String] 
[error]  case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows) 
[error]          ^

同様のエラーのようなエラーが発生しますが現れます。 mではありません。Array[String] - Aです。しかし、Scalaにこれを望ましいタイプに「翻訳」するよう指示する方法はありますか?

+1

"バウンド" によって制約することができ、 "typeパラメータを"(戻り値の型を見てください)。 –

答えて

4

あなたのADT内の個々のタイプにアクセスする簡単な方法は、抽象型メンバー代わりのジェネリック型パラメータを使用することです

sealed trait Foo { 
    type A 
    def method: A 
} 

case object Bar extends Foo { 
    type A = Array[String] 
    def method: A = Array.empty[String] 
} 

case object Baz extends Foo { 
    type A = Map[String, Array[String]] 
    def method: A = Map.empty[String, Array[String]] 
} 

case class Qux[X <: Foo](foo: X) { 
    def m: X#A = foo.method 

    // You can then pattern match on m 
    def f = m match { 
    case a: Baz.A => a.size // Use Baz#A if Baz is a class and not an object 
    case b: Bar.A => b.size // Use Bar#A if Bar is a class and not an object 
    } 
} 

それを使用して

@ Qux(Baz).m 
res6: Map[String, Array[String]] = Map() 

@ Qux(Bar).m 
res7: Array[String] = Array() 
+0

これは非常に有望なアプローチであると思われます。ありがとうございました!しかし、私はコンパイラに、「型Aは型パラメータXのメンバではない」と不平を言っているが、これは 'Qux'の定義にある。 – user4601931

+0

Hmmm ...あなたはどのバージョンを使用していますか?私は2.11.8でこれを試しました。私はちょうどそれにも別のショットを与えた。 – marios

+0

あなたのシェルに ':paste'を使い、コード全体をコピーしてコピーしてください。それはあなたのために働くのですか? – marios

3

あなたはこのようなあなたの形質に型パラメータを追加することができます。

sealed trait Foo[A] { 
    def method: A 
} 

case class Bar extends Foo[Array[String]] { 
    def method: Array[String] 
} 

case class Baz extends Foo[Map[String, Array[String]]] { 
    def method: Map[String, Array[String]] 
} 
+0

これはこれを行う最も簡単な方法です!いい答えだ。しかし、それが本当に質問の第2部分に対処しているかどうかはわかりません。 Quxはどのように機能しますか? – marios

+0

型クラス変数もそのクラスに追加します。 – Tyler

+0

を気にしないなら、 'class Qux [A]'や 'class Qux [_]'のようなものかもしれませんが、彼はそのタイプについて気にしています。 'val m = foo.method' OPは、mが' Foo [A] 'の同じタイプAであることを望んでいます。私は 'class Qux [A](foo:Foo [A])'のような何かがそのトリックをするべきだと思う。 – marios

関連する問題