键入与类型参数不匹配

时间:2017-03-16 09:21:33

标签: scala

我有以下特质:

sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

然后我定义了一个类型为Branch的变量:

val t = Branch(Branch(Leaf("S"), Leaf(2)), Leaf(99))

上面的代码运行正常。但是当我把它改为:

val t = Branch[Int](Branch(Leaf("S"), Leaf(2)), Leaf(99))

编译器抱怨:

Error:(41, 37) type mismatch;
 found   : String("S")
 required: Int
    val t = Branch[Int](Branch(Leaf("S"), Leaf(2)), Leaf(99))

当我确定第一个Branch上的类型时(在这种情况下是Int),那么节点将来自父节点?

1 个答案:

答案 0 :(得分:4)

这里发生的是,在Branch(Leaf("S"), Leaf(2))的情况下,编译器将尝试查找公共类型。对于IntString,其超类型将为Any

问题是您使用Branch[Int]强制分支定义中的类型,该类型由包含的Branch[Any]无效。删除[Int]将解决编译错误,但最终会导致Branch[Any]

让我们分析一下你的定义:

sealed trait Tree[+A]

此处+表示类型A可以是协变的。我不会详细介绍方差,因为有许多好的文章解释它(例如here)。基本上,这意味着如果StringAny的子类型,那么Tree[String]将是Tree[Any]的子类型。

case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

让我们首先注意左侧树和右侧树中A是如何出现的。那么当我们采用两种不同类型的树并将它们放在同一个分支中时会发生什么呢?

val left  = Leaf(1)   // Tree[Int]
val right = Leaf("a") // Tree[String]
val tree  = Branch(left, right) // Tree[???]

由于Aleft值中的right必须相同(这就是我们定义它的方式),编译器会尝试解决此问题。所以它问自己:IntString的第一种常见类型是什么?

       Any
        ^
       / \
      /   \
     /     \
AnyVal     AnyRef
   /         \
  Int       String
   \         /
       ...

这是Scala中的类型层次结构,非常简化。它更好地说明here。因此,StringInt的常见超类型为Any,这就是您的树将成为Tree[Any]的原因。