【问题标题】:Scala: expression's type is not compatible with formal parameter typeScala:表达式的类型与形式参数类型不兼容
【发布时间】:2017-08-02 23:41:03
【问题描述】:
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)

构建良好。所以当像这样显式传递参数时,编译器找到类型是没有问题的。

另外,从 PepperoniDefMushroomDef 中删除 Serializable 特征,如下所示:

object PepperoniDef extends ToppingDef[Papperoni] {
}

object MushroomDef extends ToppingDef[Mushroom] {
}

也可以构建。但是在我的情况下,我需要 Serializable。

如果有必要,我想我可能可以重组代码来解决这个问题,但我想了解发生了什么,我不知道为什么在第一种情况下类型不明确,或者为什么存在 Serializable 特征有任何影响。提前感谢您提供任何见解。

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

val topping =

到这里:

val topping:ToppingDef[_ <: Ingredient] =

这可以解决构建错误并且不需要更改泛型类,我希望尽可能保持简单且未注释,以便让 Scala 推断出尽可能多的类型信息。

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

【问题讨论】:

    标签: scala generics types


    【解决方案1】:

    似乎通过类型注释帮助编译器可以编译:

    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 &lt;: Ingredient 的“子类型”意味着 ToppingDef[Papperoni] &lt;: ToppingDef[Ingredient],因此允许 T 的公共超类型:

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

    而且这也可以在没有类型注释的情况下编译。

    编辑:

    使Ovens 类型参数成为存在而不是普遍量化的类型似乎与Serializable trait 一样有效:

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

    【讨论】:

    • 真正的问题是——为什么推断的类型(可以简化为Serializable with ToppingDef[_ &lt;: Ingredient])对于任何T &lt;: Ingredient 都不满足ToppingDef[T]?如果将topping 的类型设置为Serializable with ToppingDef[_ &lt;: Ingredient],则会出现相同的错误,如果将其设置为ToppingDef[_ &lt;: Ingredient] - 它可以工作。不能说为什么……你可以吗?
    • @TzachZohar 浏览规范。
    • @TzachZohar 规范中没有任何内容,我查看了-Ytyper-debug 输出,但没有提供任何信息。闻起来像虫子。
    【解决方案2】:

    你需要为你的 trait 添加一些类型信息:

    trait ToppingDef[+T <: Ingredient] {}
    

    现在topping 变量无法确定T 应该是Ingredient,所以你需要告诉它。

    【讨论】:

    • 那一种解决方法 - 但实际上没有必要,也没有解释为什么从对象中删除 extends Serializable 也会编译...
    • 谢谢。你能评论一下为什么 [+T <: ingredient>
    • + 表示协方差,您可以在文档中了解更多信息,他们有一些示例:docs.scala-lang.org/tour/variances.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-07
    • 2018-08-07
    • 1970-01-01
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多