[B >: A] 在 Scala 中做什么?
[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)
}
What does [B >: A]
mean in Scala? And what are the effects?
Example reference: 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)
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
[B >: A]
是类型下限。这意味着B
被限制为A
的超类型。类似地,
[B <: A]
是类型上限,这意味着B
被限制为A
的子类型。在您展示的示例中,您可以将
B
类型的元素推送到包含A
元素的堆栈上,但结果是一个堆栈B
元素。您看到此内容的页面实际上有一个指向另一个关于较低类型界限的页面的链接,其中包括一个显示效果的示例。
[B >: A]
is a lower type bound. It means thatB
is constrained to be a supertype ofA
.Similarly
[B <: A]
is an upper type bound, meaning thatB
is constrained to be a subtype ofA
.In the example you've shown, you're allowed to push an element of type
B
onto a stack containingA
elements, but the result is a stack ofB
elements.The page where you saw this actually has a link to another page about lower type bounds, which includes an example showing the effect.
X <: Y
表示类型参数X
必须是类型Y
的子类型。X >: Y
表示相反,X
必须是Y
的超类型(在这两种情况下,X = Y< /代码> 就可以了)。这种表示法可能与直觉相反,人们可能认为狗不仅仅是一种动物(在编程术语中更精确,更多的服务),但正是因为它更精确,狗的数量比动物的数量少,类型
Animal
比Dog
类型包含更多的值,它包含所有狗,也包含所有鸵鸟。所以动物
>:狗
。至于为什么
push
有这个签名,我不确定我能比示例来自的页面更好地解释它,但让我尝试一下。它从方差开始。
class Stack[+A]
中的+
表示Stack
在 A 中是协变的
。如果X
是Y
的子类型,则Stack[X]
将是Stack[Y]
的子类型>。一堆狗也是一堆动物。对于数学倾向的人来说,如果人们将 Stack 视为从类型到类型的函数(X 是一种类型,如果将其传递给 Stack,则会得到 Stack[X],这是另一种类型),协变意味着它是一个递增的函数(带有 <:,子类型关系是类型的顺序)。这似乎是正确的,但这并不是一个简单的问题。情况并非如此,如果使用push例程修改它,添加一个新元素,也就是说
(示例不同,push返回一个新堆栈,保持
this
不变)。当然,Stack[Dog] 应该只接受将狗推入其中。不然就不再是一堆狗了。但如果我们接受它被视为一堆动物,我们可以显然,将这个堆栈视为协变是不合理的。当堆栈被视为 Stack[Animal] 时,允许执行
Stack[Dog]
上不存在的操作。这里使用push 所做的事情可以用任何以A 作为参数的例程来完成。如果泛型类被标记为协变,使用 C[+A],则 A 不能是 C 的任何(公共)例程的任何参数的类型,编译器将强制执行这一点。但示例中的堆栈有所不同。我们将有一个
def push(a: A): Stack[A]
。如果调用push
,则获得一个新堆栈,并且原始堆栈保持不变,它仍然是一个正确的 Stack[Dog],无论已推送什么。如果我们这样做,dogs
仍然是相同的,并且仍然是Stack[Dog]
。显然newStack
不是。它也不是一个 Stack[Ostrich],因为它还包含原始堆栈中曾经(并且仍然存在)的狗。但这将是一个正确的Stack[Animal]
。如果有人推一只猫,更准确地说它是一个 Stack[Mammal](同时也是一堆动物)。如果压入12
,它只是一个Stack[Any]
,这是Dog
和Integer
的唯一公共超类型>。问题是编译器无法知道此调用是否安全,并且不允许在def push(a: A): Stack[A]< 中使用
a: A
参数/code> 如果Stack
被标记为协变。如果它停在那里,协变堆栈将毫无用处,因为无法将值放入其中。签名解决了这个问题:
如果
B
是A
的祖先,当添加一个B
时,会得到一个Stack[B]
。因此,向Stack[Dog]
添加一个Mammal
会得到一个Stack[Mammal]
,添加一个动物会得到一个Stack[Animal]
,这很好。添加狗也可以,A >:A 为真。这很好,但似乎限制太多。如果添加的项的类型不是
A
的祖先怎么办?例如,如果它是后代,例如dogs.push(goldenRetriever)
,该怎么办?不能拿B = GoldenRetriever
,也不能拿GoldenRetriever >: Dog
,反之亦然。然而,我们可以认为 B = 狗。如果参数elem预计是Dog类型,我们当然可以传递一个GoldenRetriever。一个得到一堆B,仍然是一堆狗。确实不允许B = GoldenRetriever
。结果将被输入为 Stack[GoldenRetriever],这是错误的,因为堆栈也可能包含爱尔兰 setter。鸵鸟呢?那么,Ostrich 既不是 Dog 的超类型,也不是 Dog 的子类型。但正如我们可以添加一只金毛猎犬,因为它是一只狗,并且可以添加一只狗一样,鸵鸟是一种动物,并且可以添加一只动物。因此,取 B = Animal >: Dog 有效,因此当推鸵鸟时,会得到一个 Stack[Animal]。
使堆栈协变强制此签名,比简单的
push(a: A) : Stack[A]
更复杂。但是我们获得了一个完全灵活的例程,可以添加任何内容,而不仅仅是A
,并且可以尽可能精确地输入结果。除了类型声明之外,实际的实现与push(a: A)
相同。X <: Y
means type parameterX
must be a subtype of typeY
.X >: Y
means the opposite,X
must be a super type ofY
(in both cases,X = Y
is ok). This notation can be contrary intuition, one may think a dog is more than an animal (more precise of in programming terms, more services), but for the very reason it is more precise, there are less dogs than there are animals, the typeAnimal
contains more values than the typeDog
, it contains all dogs, and all ostriches too. SoAnimal
>:Dog
.As for the reason why
push
has this signature, I'm not sure I can explain it better than the page the example comes from, but let me try.It starts with variance. The
+
inclass Stack[+A]
means thatStack
iscovariant in A
. ifX
is a subtype ofY
,Stack[X]
will be a subtype ofStack[Y]
. A stack of dogs is also a stack of animals. For the mathematically inclined, if one sees Stack as a function from type to type (X is a type, if you pass it to Stack, you get Stack[X], which is another type), being covariant means that it is an increasing function (with <:, the subtyping relation being the orders on types).This seems right, but this is not such an easy question. It would not be so, with a push routine that modifies it, adding a new element, that is
(the example is different, push returns a new stack, leaving
this
unchanged). Of course, a Stack[Dog] should only accept dogs to be pushed into it. Otherwise, it would no longer be a stack of dogs. But if we accept it to be treated as a stack of animals, we could doClearly, treating this stack as covariant is unsound. When the stack is seen as a
Stack[Animal]
, an operation is allowed that would not be onStack[Dog]
. What was done here with push can be done with any routine that takes A as its argument. If a generic class is marked as covariant, with C[+A], then A cannot be the type of any argument of any (public) routine of C, and the compiler will enforce that.But the stack in the exemple is different. We would have a
def push(a: A): Stack[A]
. If one callspush
, one gets a new stack, and the original stack is left unchanged, it is still a proper Stack[Dog], whatever may have been pushed. If we dodogs
is still the same and still aStack[Dog]
. ObviouslynewStack
is not. Nor is it aStack[Ostrich]
, because it also contains the dogs that were (and still are) in the original stack. But it would be a properStack[Animal]
. If one pushes a cat, it would be more precise to say it is aStack[Mammal]
(while being a stack of animals too). If one pushes12
, it will be only aStack[Any]
, the only common supertype ofDog
andInteger
. The problem is that the compiler has no way to know that this call is safe, and will not allow thea: A
argument indef push(a: A): Stack[A]
ifStack
is marked covariant. If it stopped there, a covariant stack would be useless because there would be no way to put values in it.The signature solves the problem:
If
B
is an ancestor ofA
, when adding aB
, one gets aStack[B]
. So adding aMammal
to aStack[Dog]
gives aStack[Mammal]
, adding an animal gives aStack[Animal]
, which is fine. Adding a Dog is ok too, A >: A is true.This is good, but seems too restrictive. What if the added item's type is not an ancestor of
A
? For instance, what if it is a descendant e.gdogs.push(goldenRetriever)
. One cannnot takeB = GoldenRetriever
, one has notGoldenRetriever >: Dog
, but the opposite. Yet, one can take B = Dog all right. It the parameter elem is expected to be of type Dog, we can pass of course pass a GoldenRetriever. One gets a stack of B, still a stack of dogs. And it is right thatB = GoldenRetriever
was not allowed. The result would have been typed asStack[GoldenRetriever]
, which would be wrong because the stack may have contained irish setters too.What about ostrishes? Well
Ostrich
is neither an supertype, nor a subtype ofDog
. But just as one can add a goldenRetriever because it is a dog, and it is possible to add a dog, an ostrich is an animal, and it is possible to add an animal. So taking B = Animal >: Dog works, and so a when pushing an ostrich, one gets aStack[Animal]
.Making the stack covariant force this signature, more complex than the naïve
push(a: A) : Stack[A]
. But we gain a routine that is completely flexible, anything can be added, not just anA
, and yet, types the result as precisely as can be. And the actual implementation, except for the types declarations, is the same it would have been withpush(a: A)
.作为一个很好的概述,请参阅 @retronym 的 git 页面
As a great overview, see the git page of @retronym