子类型和类型强制

时间:2016-10-01 08:56:56

标签: scala types

下面的T类型是Fruit的子类型。 Apple也是Fruit的子类型。因此,当下面使用类型归属时,为什么编译器不强制Apple转向T.为什么我们需要一个明确的演员才能使其发挥作用?推断类型时强制的规则是什么?

trait Fruit
case class Apple(id: String) extends Fruit

type T <: Fruit

val t: T = Apple("apple") // why doesn't the compiler coerce Apple to T?

val t: T = Apple("apple").asInstanceOf[T] // works!

2 个答案:

答案 0 :(得分:1)

我认为您混淆了定义 type T <: Fruit的含义:它是抽象类型定义(与type T = Fruit不同,它是类型别名)。 Absract类型不创建扩展Fruit的类T,而是一个抽象声明,必须在某个子类中重写以供使用。

来自Learning Scala, chapter 10

  

...抽象类型是可以解析为零,一个或多个类的规范。它们以类似的方式工作以输入别名,但作为规范,它们是抽象的,不能用于创建实例

请注意,声明type T <: Fruit只能驻留在类/特征中(不是对象,也不是顶级定义),因为在扩展类/特征之前它是无意义的。在定义的地方,它仍然是抽象的,因此编译器无法确定Apple是否扩展了它。

使用这种定义的一个例子:

trait Price {
  type T <: Fruit
  def printPrice(x: T): Unit
}

class ApplePrice extends Price {
  override type T = Apple
  override def printPrice(x: Apple): Unit = ???
} 

在这里,ApplePrice必须用某些东西覆盖这个抽象类型。 Price的另一个子类可能会用不同的东西(例如Banana extends Fruit)覆盖它,这应该清楚地表明放在val t: T = Apple("apple")中的表达式Price无法编译 - 对于某些可能的扩展类,T不是AppleApple也不会扩展T

答案 1 :(得分:1)

让我们简化一下。与胎儿一起用梨和水果来代替。

 trait Fetus
 class Apple extends Fetus
 type Pear <: Fetus

 val t: Pear = new Apple()

我们宣布了Fetus,然后我们说&#34; Apple是Fetus&#34;然后&#34; Pear是Fetus&#34;。 到目前为止我们的逻辑是正确的但后来我们尝试将苹果放入梨子盒中#34;这是我们的错误。

让我们考虑另一个例子:

trait Fetus
class Apple extends Fetus
type Fruit >: Apple <: Fetus

val t: Fruit = new Apple()

在这里我们宣布了胎儿,然后我们说&#34; Apple是Fetus&#34;然后我们说&#34; Fruit是Apple和Fetus之间的中间产品。换句话说,我们构建了以下层次结构:胎儿 - &gt;水果 - &gt;苹果。因此,现在Apple是Fruit的子类型,你可以将苹果放入水果盒中,并且#34;。

更新:

Pear肯定不能和Apple一样,尽管它们都是动物,但猫不能成为狗。您可以仅按层次结构垂直但不横向:

Creature
   |
Animal
  _|_
 /    \
Dog    Cat
        \
       White Cat