Scala:表达式的类型与形式参数类型

时间:2017-08-02 13:52:23

标签: scala generics types

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。这个例子有点做作,但我已经删除了与问题无关的所有内容,以提供一个简洁的例子。

我在最后一行得到的错误是:

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)

建立良好。因此,当参数像这样明确地传递时,编译器找到类型没有问题。

另外,从SerializablePepperoniDef移除MushroomDef特征,如下所示:

object PepperoniDef extends ToppingDef[Papperoni] {
}

object MushroomDef extends ToppingDef[Mushroom] {
}

也建立。但是在我的情况下,我需要Serializable。

我认为我可以重新构建代码以便在必要时解决这个问题,但我想了解发生了什么,我不知道为什么类型在第一种情况下是模糊的,或者为什么存在Serializable特征有任何影响。提前感谢任何见解。

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

val topping =

到此:

val topping:ToppingDef[_ <: Ingredient] =

它修复了构建错误,并且不需要更改泛型类,我希望尽可能简单地保留这些类,以便让Scala尽可能多地推断类型信息。

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

2 个答案:

答案 0 :(得分:3)

似乎使用类型注释帮助编译器进行编译:

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

我不认为这与Serializable类具体有关,它似乎是编译器的怪癖,因为生成的类型具有混合类型,包括Product with Serializable。< / p>

你也可以'放松&#34;通过制作T协变来表示类型签名,意味着Topping[Ingredient]将被推断。这是因为&#34;是&#34;的子类型。协变Papperoni <: Ingredient上的关系ToppingDef[+T]表示ToppingDef[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)

答案 1 :(得分:1)

您需要在特征中添加一些类型信息:

trait ToppingDef[+T <: Ingredient] {}

目前topping变量无法确定T应该是Ingredient,因此您需要告诉它。