[B >: A] 在 Scala 中做什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7759361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does [B >: A] do in Scala?
提问by Jay Taylor
What does [B >: A]mean in Scala? And what are the effects?
[B >: A]在 Scala中是什么意思?有什么影响?
Example reference: http://www.scala-lang.org/node/129
示例参考: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)
}
回答by Don Roby
[B >: A]is a lower type bound. It means that Bis constrained to be a supertype of A.
[B >: A]是一个下限类型。这意味着B被限制为 的超类型A。
Similarly [B <: A]is an upper type bound, meaning that Bis constrained to be a subtype of A.
同样[B <: A]是类型上限,这意味着它B被限制为 的子类型A。
In the example you've shown, you're allowed to push an element of type Bonto a stack containing Aelements, but the result is a stack of Belements.
在您展示的示例中,您可以将 type 元素推B送到包含A元素的堆栈上,但结果是一个B元素堆栈。
The page where you saw this actually has a link to another page about lower type bounds, which includes an example showing the effect.
您看到的页面实际上有一个链接到另一个关于下限类型的页面,其中包含一个显示效果的示例。
回答by Didier Dupont
X <: Ymeans type parameter Xmust be a subtype of type Y. X >: Ymeans the opposite, Xmust be a super type of Y(in both cases, X = Yis 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 type Animalcontains more values than the type Dog, it contains all dogs, and all ostriches too. So Animal>: Dog.
X <: Y意味着类型参数X必须是 type 的子类型Y。X >: Y意思相反,X必须是超类型的Y(在这两种情况下,X = Y都可以)。这种表示法可能与直觉相反,人们可能认为狗不仅仅是动物(在编程术语中更精确,更多服务),但正是因为它更精确,狗比动物少,类型Animal包含比 type 更多的值Dog,它包含所有的狗和所有的鸵鸟。所以Animal>: Dog。
As for the reason why pushhas this signature, I'm not sure I can explain it better than the page the example comes from, but let me try.
至于为什么push有这个签名,我不确定我是否可以比示例来自的页面更好地解释它,但让我尝试一下。
It starts with variance. The +in class Stack[+A]?means that Stackis covariant in A. if Xis a subtype of Y, Stack[X]?will be a subtype of Stack[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).
它从差异开始。在+中class Stack[+A]?意味着Stack是covariant in A。如果X是 的子类型Y,Stack[X]? 将是 的子类型Stack[Y]。一堆狗也是一堆动物。对于数学上的倾向,如果人们将 Stack 视为从类型到类型的函数(X 是一种类型,如果将其传递给 Stack,则会得到 Stack[X],这是另一种类型),协变意味着它是一个递增的函数(使用 <:,子类型关系是类型的顺序)。
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
这似乎是对的,但这并不是一个简单的问题。事实并非如此,使用一个修改它的推送例程,添加一个新元素,即
def push(a: A): Unit
(the example is different, push returns a new stack, leaving thisunchanged). 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 do
(示例不同,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!
Clearly, treating this stack as covariant is unsound. When the stack is seen as a Stack[Animal], an operation is allowed that would not be on Stack[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.
显然,将此堆栈视为协变是不合理的。当堆栈被视为 a 时Stack[Animal],允许进行不会 on 的操作Stack[Dog]。这里用 push 完成的事情可以用任何以 A 作为参数的例程来完成。如果泛型类被标记为协变,使用 C[+A],则 A 不能是 C 的任何(公共)例程的任何参数的类型,编译器将强制执行。
But the stack in the exemple is different. We would have a def push(a: A): Stack[A]. If one calls push, 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 do
但是示例中的堆栈不同。我们会有一个def push(a: A): Stack[A]. 如果调用push,则获得一个新堆栈,而原始堆栈保持不变,它仍然是一个正确的 Stack[Dog],无论是否被压入。如果我们这样做
val newStack = dogs.push(ostrich)
dogsis still the same and still a Stack[Dog]. Obviously newStackis not. Nor is it a Stack[Ostrich], because it also contains the dogs that were (and still are) in the original stack. But it would be a proper Stack[Animal]. If one pushes a cat, it would be more precise to say it is a Stack[Mammal](while being a stack of animals too). If one pushes 12, it will be only a Stack[Any], the only common supertype of Dogand Integer. The problem is that the compiler has no way to know that this call is safe, and will not allow the a: Aargument in def push(a: A): Stack[A]if Stackis marked covariant. If it stopped there, a covariant stack would be useless because there would be no way to put values in it.
dogs仍然是一样的,仍然是Stack[Dog]. 显然newStack不是。也不是Stack[Ostrich],因为它还包含曾经(现在仍然)在原始堆栈中的狗。但这将是一个适当的Stack[Animal]. 如果有人推一只猫,更准确地说它是一只Stack[Mammal](同时也是一堆动物)。如果一个push 12,它只会是andStack[Any]的唯一公共超类型。问题是编译器无法知道这个调用是安全的,并且不允许if 中的参数被标记为协变。如果它停在那里,协变堆栈将毫无用处,因为无法将值放入其中。DogIntegera: Adef push(a: A): Stack[A]Stack
The signature solves the problem:
签名解决了这个问题:
def push[B >: A](elem: B): Stack[B]
If Bis an ancestor of A, when adding a B, one gets a Stack[B]. So adding a Mammalto a Stack[Dog]gives a Stack[Mammal], adding an animal gives a Stack[Animal], which is fine. Adding a Dog is ok too, A >: A is true.
如果B是 的祖先A,则添加 a 时B,会得到 a Stack[B]。因此,将 a 添加Mammal到 aStack[Dog]给出 a Stack[Mammal],添加一个动物给出 a Stack[Animal],这很好。加一只狗也可以,A >: A 是真的。
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.g dogs.push(goldenRetriever). One cannnot take B = GoldenRetriever, one has not GoldenRetriever >: 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 that B = GoldenRetrieverwas not allowed. The result would have been typed as Stack[GoldenRetriever], which would be wrong because the stack may have contained irish setters too.
这很好,但似乎太严格了。如果添加的项目的类型不是 的祖先A怎么办?例如,如果它是后代,例如dogs.push(goldenRetriever). 一个不能拿B = GoldenRetriever,一个没有GoldenRetriever >: Dog,而是相反。然而,你可以认为 B = Dog 没问题。如果参数 elem 应该是 Dog 类型,我们当然可以传递一个 GoldenRetriever。一个得到一堆B,仍然是一堆狗。这是B = GoldenRetriever不允许的。结果将被输入为Stack[GoldenRetriever],这是错误的,因为堆栈可能也包含爱尔兰语 setter。
What about ostrishes? Well Ostrichis neither an supertype, nor a subtype of Dog. 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 a Stack[Animal].
鸵鸟呢?WellOstrich既不是Dog. 但正如可以添加一只金毛猎犬,因为它是一只狗,并且可以添加一只狗,鸵鸟是一种动物,并且可以添加一种动物。所以取 B = Animal >: Dog 是有效的,所以当推鸵鸟时,一个Stack[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 an A, 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 with push(a: A).
使堆栈协变强制此签名,比 na?ve 更复杂push(a: A) : Stack[A]。但是我们获得了一个完全灵活的例程,可以添加任何内容,而不仅仅是A,并且尽可能精确地键入结果。除了类型声明之外,实际的实现与push(a: A).

