[B >: A]
在Scala 中意味着什么?有什么影响?
示例参考:http://www.scala-lang.org/node/129
class Stack[+A] {
def push[B >: A](elem: B): Stack[B] = new Stack[B] {
override def top: B = elem
override def pop: Stack[B] = Stack.this
override def toString() = elem.toString() + " " + Stack.this.toString()
}
def top: A = error("no element on stack")
def pop: Stack[A] = error("no element on stack")
override def toString() = ""
}
object VariancesTest extends Application {
var s: Stack[Any] = new Stack().push("hello");
s = s.push(new Object())
s = s.push(7)
println(s)
}
Run Code Online (Sandbox Code Playgroud)
Did*_*ont 18
X <: Y
表示类型参数X
必须是类型的子类型Y
.X >: Y
意思是相反,X
必须是超级类型Y
(在两种情况下X = Y
都可以).这种符号可能是相反的直觉,人们可能会认为狗不仅仅是一种动物(在编程术语中更精确,更多的服务),但由于其原因它更精确,狗的数量少于动物,类型Animal
包含的值多于类型Dog
,它包含所有狗,也包含所有鸵鸟.所以Animal
> : Dog
.
至于为什么push
有这个签名的原因,我不确定我能比这个例子的页面更好地解释它,但让我试试.
它以差异开始.将+
在class Stack[+A]
指Stack
是covariant in A
.if X
是子类型Y
,Stack[X]
将是子类型Stack[Y]
.一堆狗也是一堆动物.对于数学倾向,如果将Stack视为一个函数从一个类型到另一个类型(X是一个类型,如果你将它传递给Stack,你得到Stack [X],这是另一种类型),covariant意味着它是一个增加function(带<:,子类型关系是类型上的订单).
这似乎是正确的,但这不是一个容易的问题.它不会是这样,使用推送例程修改它,添加一个新元素,即
def push(a: A): Unit
Run Code Online (Sandbox Code Playgroud)
(示例不同,push返回一个新堆栈,保持this
不变).当然,Stack [Dog]应该只接受狗被推进去.否则,它将不再是一堆狗.但如果我们接受它被视为一堆动物,我们就可以做到
val dogs : Stack[Dog] = new Stack[Dog]
val animals : Stack[Animal] = dogs // if we say stack is covariant
animals.push(ostrich) // allowed, we can push anything in a stack of any.
val topDog: Dog = dogs.top // ostrich!
Run Code Online (Sandbox Code Playgroud)
显然,将此堆栈视为协变是不合理的.当堆栈被视为a时Stack[Animal]
,允许一个不会打开的操作Stack[Dog]
.这里用push执行的操作可以使用任何以A作为参数的例程来完成.如果泛型类被标记为协变,使用C [+ A],则A不能是C的任何(公共)例程的任何参数的类型,并且编译器将强制执行该操作.
但是例子中的堆栈是不同的.我们会有一个def push(a: A): Stack[A]
.如果一个人调用push
,就会得到一个新的堆栈,并且原始堆栈保持不变,它仍然是一个正确的Stack [Dog],无论什么可能被推送.如果我们这样做
val newStack = dogs.push(ostrich)
Run Code Online (Sandbox Code Playgroud)
dogs
仍然是一样的,仍然是一个Stack[Dog]
.显然newStack
不是.也不是一个Stack[Ostrich]
,因为它还包含原始堆栈中(并且仍然是)的狗.但这将是一个恰当的Stack[Animal]
.如果一个人推猫,那就更准确地说它是一个Stack[Mammal]
(同时也是一堆动物).如果一个推动12
,它将只是一个Stack[Any]
,唯一的常见超类型Dog
和Integer
.问题是编译器无法知道此调用是否安全,并且不允许if中的a: A
参数标记为covariant.如果它停在那里,协变栈将无用,因为没有办法将值放入其中.def push(a: A): Stack[A]
Stack
签名解决了这个问题:
def push[B >: A](elem: B): Stack[B]
Run Code Online (Sandbox Code Playgroud)
如果B
是A
添加a 的祖先B
,则获得a Stack[B]
.因此,加入Mammal
到Stack[Dog]
给出了一个Stack[Mammal]
,增加动物给出了一个Stack[Animal]
,这是罚款.添加狗也可以,A>:A是真的.
这很好,但似乎限制太多.如果添加的项目的类型不是其祖先A
怎么办?例如,如果它是后代,例如dogs.push(goldenRetriever)
.一个B = GoldenRetriever
人没有,一个没有GoldenRetriever >: Dog
,但相反.然而,人们可以把B = Dog做好.参数elem预计是Dog类型,我们当然可以通过一个GoldenRetriever.一个人获得一堆B,仍然是一堆狗.这是不对的B = GoldenRetriever
.结果将被输入为Stack[GoldenRetriever]
,这将是错误的,因为堆栈可能也包含爱尔兰的setter.
什么是东西?好吧Ostrich
既不是超类型,也不是亚型Dog
.但正如人们可以添加一个goldenRetriever,因为它是一只狗,并且可以添加一只狗,一只鸵鸟是一种动物,并且可以添加动物.因此,采取B =动物>:狗工作,所以当推动鸵鸟,一个得到一个Stack[Animal]
.
使堆栈协变迫使这个签名,比天真更复杂push(a: A) : Stack[A]
.但是我们获得了一个完全灵活的例程,任何东西都可以添加,而不仅仅是一个A
,然而,尽可能精确地输入结果.除了类型声明之外,实际的实现与它本来一样push(a: A)
.
归档时间: |
|
查看次数: |
9985 次 |
最近记录: |