下面的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!
答案 0 :(得分:1)
我认为您混淆了定义
type T <: Fruit
的含义:它是抽象类型定义(与type T = Fruit
不同,它是类型别名)。 Absract类型不创建扩展Fruit
的类T,而是一个抽象声明,必须在某个子类中重写以供使用。
...抽象类型是可以解析为零,一个或多个类的规范。它们以类似的方式工作以输入别名,但作为规范,它们是抽象的,不能用于创建实例
请注意,声明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
不是Apple
,Apple
也不会扩展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