2017-08-02 17 views
3
trait Ingredient{} 

case class Papperoni() extends Ingredient{} 
case class Mushroom() extends Ingredient{} 

trait ToppingDef[T] { 
} 

object PepperoniDef extends Serializable with ToppingDef[Papperoni] { 
} 

object MushroomDef extends Serializable with ToppingDef[Mushroom] { 
} 

class Oven[T <: Ingredient](val topping:ToppingDef[T]) { 
} 

class Pizza { 
    def cook = { 
    val topping = 
     if(someCondition()) { PepperoniDef } 
     else { MushroomDef} 

    new Oven(topping) // <-- build error here 
    } 
} 

私はScala 2.11を使用しています。この例は多少の工夫をしていますが、私は問題に無関係なものをすべて取り除いて、簡潔な例を提供しています。Scala:式の型が仮パラメータ型と互換性がありません

私は最後の行に乗るエラーは次のとおりです。

Error:(26, 5) no type parameters for constructor Oven: (topping: ToppingDef[T])Oven[T] exist so that it can be applied to arguments (Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient]) 
--- because --- 
argument expression's type is not compatible with formal parameter type; 
found : Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient] 
required: ToppingDef[?T] 
    new Oven(topping) 

は、しかし、たとえばこれに最後の行を変更:

new Oven(PepperoniDef) 

は罰金構築します。したがって、コンパイラは、パラメータが明示的にこのように渡されたときに型の検索に問題はありません。

また、このようPepperoniDefMushroomDefからSerializable形質を取り除く:

object PepperoniDef extends ToppingDef[Papperoni] { 
} 

object MushroomDef extends ToppingDef[Mushroom] { 
} 

も構築します。しかし私の場合、私はSerializableが必要です。

私はおそらく、これを回避するコードを必要に応じて再構成することができますが、何が起こっているのか理解したいと思います。なぜ型が最初のケースであいまいか、形質には何の効果もありません。あらゆる洞察に感謝します。

編集:非常に参考にしていただきありがとうございます。これに

val topping = 

:私が最も簡潔な修正がこれを変更することだと思うビルドエラーが硬化し、私は維持したいと思いますジェネリッククラスへの変更を必要としない

val topping:ToppingDef[_ <: Ingredient] = 

Scalaができるだけ多くの型情報を推論できるように、できるだけシンプルではない。

Serializableの存在がこれに何らかの影響を与える理由についての質問には答えません。

答えて

3

このコンパイルを行っ型注釈でコンパイラを手伝いするようだ:私はこれは、具体的Serializableクラスに関係しているとは思わない

val topping: ToppingDef[ 
    _ >: Papperoni with Mushroom <: Ingredient with Product with Serializable] = 
    if (true) { 
     PepperoniDef 
    } else { 
     MushroomDef 
    } 

、それはので、私には、コンパイラの癖のように思えますとにかくProduct with Serializableを含むタイプが混在しています。

また、タイプシグネチャをTの共変にすることで、「Topping[Ingredient]」と推測できます。

trait ToppingDef[+T] 
val topping: ToppingDef[Ingredient with Product with Serializable] = 
    if (true) { 
    PepperoniDef 
    } else { 
    MushroomDef 
    } 

これも型注釈なしでコンパイルされます。これTのための共通のスーパータイプを許可する、ToppingDef[Papperoni] <: ToppingDef[Ingredient]を意味共変ToppingDef[+T]に関係Papperoni <: Ingredient「のサブタイプである」ためです

編集:代わりに普遍的に定量化されたタイプの実存Ovenの型パラメータを作成

Serializable形質と同様に動作するようです:

class Oven[_ <: Ingredient](val topping: ToppingDef[_]) 

val topping: Serializable with ToppingDef[_ <: Ingredient] = 
    if (true) { 
    PepperoniDef 
    } else { 
    MushroomDef 
    } 

new Oven(topping) 
+0

本当の質問は、 (ToppingDef [_ <:Ingredient] 'で簡略化することができます)は、' T <:Ingredient'に対して 'ToppingDef [T]'を満たしていませんか? 'Topping'の型を' ToppingDef [_ <:Ingredient] 'に設定した場合、ToppingDef [_ <:Ingredient>'に設定すると同じエラーが発生します。なぜ...あなたはできますか? –

+0

@TzachZoharスペックを見る。 –

+2

@TzachZohar仕様には何も書かれていませんが、私は '-Ytyper-debug'出力を見ましたが、有益な情報は出ませんでした。バグのようなにおいがする。 –

1

あなたの形質にいくつかのタイプの情報を追加する必要があります。

trait ToppingDef[+T <: Ingredient] {} 

topping変数はTはあなたがそれを指示する必要がありそうIngredientことになっていることを把握することができません。

+1

1つの回避策 - しかし、それは実際には必要ありませんし、それオブジェクトから 'extends Serializable'を取り除くことがなぜコンパイルされるのか説明しません。 –

+0

ありがとう。 [+ T <:Ingredient]がビルドエラーを修正する理由についてコメントできますか?[T <:Ingredient]はなぜですか? –

+0

'+'は共分散を示していますが、ドキュメントで詳細を読むことができます。http://docs.scala-lang.org/tour/variances.html – Tyler

関連する問題