Scala:表达式的类型与正式参数类型不兼容

问题描述:

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。

我想我可以重新调整代码来解决这个问题(如果有必要),但我想知道发生了什么,我不知道为什么类型在第一种情况下是模棱两可的,或者为什么存在Serializable特质有什么作用。预先感谢任何见解。

编辑:谢谢你的回复,非常有帮助。我认为最简洁的解决方法是可以改变:

val topping = 

这样:

val topping:ToppingDef[_ <: Ingredient] = 

,其固化生成错误,并且不需要更改通用类,我想保持尽可能简单地进行无注释,以便让Scala尽可能多地推断出类型信息。

这并不回答为什么Serializable的存在对此有任何影响的问题。

看来,帮助编译出与类型注释使这个编译:

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

我不认为这有与Serializable类专门做,它似乎是一个编译器的怪癖我自无论如何,生产型都有混合型,包括Product with Serializable

您也可以通过制作T协变来“放松”型签名,意味着Topping[Ingredient]将被推断。这是因为在协ToppingDef[+T]“是子类型”关系Papperoni <: IngredientToppingDef[Papperoni] <: ToppingDef[Ingredient],从而使共同的父T

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

,这也编译没有类型注释。

编辑:

制作Oven S型参数的存在,而不是普遍的量化类型似乎与Serializable性状以及工作:

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

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

new Oven(topping) 
+0

真正的问题是 - 为什么推断类型(可以简化为'使用ToppingDef [_

+0

@TzachZohar阅读规范。 –

+2

@TzachZohar没有规范,我看着'-Ytyper-debug'输出,但没有提供任何信息。闻起来像一个错误。 –

你需要某种类型的信息添加到您的特点:

trait ToppingDef[+T <: Ingredient] {} 

眼下topping变量无法弄清楚,T应该是一个Ingredient,所以你需要告诉它。

+1

一个解决办法 - 但它实际上没有必要,它并没有解释为什么从对象中删除'extends Serializable'还要编译... –

+0

谢谢。你可以评论为什么[+ T

+0

'+'表示协变性,你可以在文档中阅读更多关于它的信息,他们有一些例子:http://docs.scala-lang.org/tour/variances.html – Tyler