[英]What does [B >: A] do in 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)
}
[B >: A]
是较低类型的界限。 这意味着B
被约束为A
的超类型。
类似地, [B <: A]
是上限类型,意味着B
被约束为A
的子类型。
在您已经显示的示例中,您可以将类型B
的元素推送到包含A
元素的堆栈中,但结果是一堆B
元素。
您看到它的页面实际上有一个指向另一个关于较低类型边界的页面的链接,其中包含一个显示效果的示例。
X <: Y
表示类型参数X
必须是类型的子类型Y
。 X >: Y
表示相反, X
必须是Y
的超类型(在两种情况下, X = Y
都可以)。 这种符号可能是相反的直觉,人们可能会认为狗不仅仅是一种动物(在编程术语中更精确,更多的服务),但由于其原因它更精确,狗的数量少于动物,类型Animal
包含的数值比Dog
类更多,它包含所有狗,也包含所有鸵鸟。 所以Animal
>: Dog
。
至于push
有这个签名的原因,我不确定我能比这个例子的页面更好地解释它,但让我试试。
它以差异开始。 的+
在class Stack[+A]
意味着Stack
是covariant in A
。 如果X
是Y
的子类型,则Stack[X]
将是Stack[Y]
的子类型。 一堆狗也是一堆动物。 对于数学倾向,如果将Stack视为一个函数从一个类型到另一个类型(X是一个类型,如果你把它传递给Stack,你得到Stack [X],这是另一种类型),covariant意味着它是一个增加function(带<:,子类型关系是类型上的订单)。
这似乎是正确的,但这不是一个容易的问题。 它不会是这样,使用推送例程修改它,添加一个新元素,即
def push(a: A): Unit
(例子中是不同的,推返回一个新的堆栈,留下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!
显然,将此堆栈视为协变是不合理的。 当堆栈被视为Stack[Animal]
,允许不在Stack[Dog]
。 这里用push执行的操作可以使用任何以A作为参数的例程来完成。 如果泛型类被标记为协变,使用C [+ A],则A不能是C的任何(公共)例程的任何参数的类型,并且编译器将强制执行该操作。
但是例子中的堆栈是不同的。 我们会有一个def push(a: A): Stack[A]
。 如果一个人调用push
,就会得到一个新的堆栈,并且原始堆栈保持不变,它仍然是一个正确的Stack [Dog],无论什么可能被推送。 如果我们这样做
val newStack = dogs.push(ostrich)
dogs
仍然是相同的,仍然是Stack[Dog]
。 显然newStack
不是。 它也不是Stack[Ostrich]
,因为它还包含原始堆栈中(现在仍然是)的狗。 但它将是一个合适的Stack[Animal]
。 如果一个人推猫,那就更准确地说它是一个Stack[Mammal]
(同时也是一堆动物)。 如果一个推12
,它将只是一个Stack[Any]
,这是Dog
和Integer
唯一常见的超类型。 问题是编译器无法知道此调用是否安全,并且不允许a: A
def push(a: A): Stack[A]
a: A
参数def push(a: A): Stack[A]
如果Stack
被标记为协变,则Stack
def push(a: A): Stack[A]
。 如果它停在那里,协变栈将无用,因为没有办法将值放入其中。
签名解决了这个问题:
def push[B >: A](elem: B): Stack[B]
如果B
是A
的祖先,则在添加B
,会得到一个Stack[B]
。 因此,将一个Mammal
添加到Stack[Dog]
会给出一个Stack[Mammal]
,添加一个动物会给出一个Stack[Animal]
,这很好。 添加狗也可以,A>:A是真的。
这很好,但似乎限制太多。 如果添加的项目的类型不是A
的祖先怎么办? 例如,如果它是后代,例如dogs.push(goldenRetriever)
,该怎么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]
更复杂push(a: A) : Stack[A]
。 但是我们获得了一个完全灵活的例程,可以添加任何东西,而不仅仅是A
,然而,尽可能精确地输入结果。 除了类型声明之外,实际的实现与push(a: A)
。
作为一个很好的概述,请参阅@retronym的git页面
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.