为什么表达继承有不同的表示法?在泛型中,我必须使用<: - 运算符 - 在普通的类继承中,我必须使用extends关键字。
例如我必须写这个:
class X[A <: B] extends Y
但为什么不写这样的东西:
class X[A <: B] <: Y
或
class X[A extends B] extends Y // like Java
我对当前符号没有任何问题,但我想知道是否有理由以不同的方式标记泛型的类型层次结构。
答案 0 :(得分:13)
嗯,没有什么停止 Scala这样做了,但是,事实上,他们并没有表达同样的事情。。事实上,您可以在Java中看到X super Y
,但您不能说class X super Y
。
关键字extends
表示继承之一 classes 之间的关系。另一方面,<:
和>:
表示 types 之间的关系,其中一个是边界。当我说X <: Y
时,X
和Y
对String
有效,例如,String extends String
毫无意义。情况也是List[String] <: List[AnyRef]
,但List[String] extends List[AnyRef]
再次无意义。并且,为了说明问题,Set[String] <: Set[AnyRef]
为真。在所有这些示例中,我只是说我们正在讨论相同的类,但不一定是关于相同的类型。
当然,类型之间还存在其他关系,例如视图边界(<%
)和上下文边界(:
)。
因此,仅仅因为extends
暗示<:
,它并不遵循<:
暗示extends
,仅此一项就足以避免使用相同的关键字。再加上类型之间的其他关系,你几乎已经完成了一笔交易。
答案 1 :(得分:7)
当你扩展(没有双关语)你的例子超出一个简单的情况时,它会变得更加明显。
多重继承:
class C[A] extends X with Y with Z
的混入:
val x = new X with Y
参数:
class X[A <: B] extends Y[A]
多个(相关)类型参数:
class X[A >: B, B](x: A, xs: Seq[B])
上下文界限:
class X[A : Manifest]
查看边界:
class X[A <% Ordered[A]]
通用方法:
class Foo[B] {
def bar[A <: B](x: A) = ...
}
正如您所看到的,可以在类型参数中指定的关系比声明类时可用的简单线性层次结构更丰富,尤其是当您允许边界时。
值得注意的是,通常会推断类或方法的泛型类型参数,允许您编写:
val myList = List(1, 2, 3)
而不是
val myList = List[Int](1, 2, 3)
因此,使用符号的方式非常不同。
<强>更新强>
一个特别的例子只是浮现在脑海中,展示了同时使用这两种符号并展示它们如何保持清晰:
def someMethod[T <: Foo with Bar](x: T) = ...
这要求type-param T
是一个混合在Foo
和Bar
中的子类型。
同样适用于结构类型:
type Closable = { def close: Unit } //alias for a structural type
def someMethod[T <: Closable](x: T) = ...