Scala 中 A<:B 和 +B 有什么区别?

发布于 2024-10-09 00:43:35 字数 111 浏览 5 评论 0 原文

Scala有什么区别

[A <: B]

Scala和

[+B]

What's the difference between

[A <: B]

and

[+B]

in Scala?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

三生路 2024-10-16 00:43:35

Q[A <: B] 表示类 Q 可以采用任何属于 B 子类的类 A >。

Q[+B] 表示 Q 可以采用任何类,但如果 A 的子类>B,则 Q[A] 被认为是 Q[B] 的子类。

Q[+A <: B] 表示类 Q 只能获取 B 的子类并传播子类关系。

当您想做一些通用的事情,但需要依赖 B 中的一组特定方法时,第一个很有用。例如,如果您有一个带有 toFile 方法的 Output 类,则可以在任何可传递到 Q 的类中使用该方法。

当您想要使集合的行为与原始类相同时,第二个很有用。如果您使用 B 并创建子类 A,那么您可以在需要 B 的任何地方传递 A 。但是如果你获取BQ[B]集合,是不是总能传入Q[A ] 代替?一般来说,不会;在某些情况下,这样做是错误的。但您可以说,使用 +B (协方差;Q covaries--follows with--B的子类的继承关系)。

Q[A <: B] means that class Q can take any class A that is a subclass of B.

Q[+B] means that Q can take any class, but if A is a subclass of B, then Q[A] is considered to be a subclass of Q[B].

Q[+A <: B] means that class Q can only take subclasses of B as well as propagating the subclass relationship.

The first is useful when you want to do something generic, but you need to rely upon a certain set of methods in B. For example, if you have an Output class with a toFile method, you could use that method in any class that could be passed into Q.

The second is useful when you want to make collections that behave the same way as the original classes. If you take B and you make a subclass A, then you can pass A in anywhere where B is expected. But if you take a collection of B, Q[B], is it true that you can always pass in Q[A] instead? In general, no; there are cases when this would be the wrong thing to do. But you can say that this is the right thing to do by using +B (covariance; Q covaries--follows along with--B's subclasses' inheritance relationship).

百合的盛世恋 2024-10-16 00:43:35

我想用更多的例子来扩展 Rex Kerr 的出色答案
假设我们有四个类:

 class Animal {}
 class Dog extends Animal {}

 class Car {}
 class SportsCar extends Car {}

让我们从方差开始:

 case class List[+B](elements: B*) {} // simplification; covariance like in original List

 val animals: List[Animal] = List( new Dog(), new Animal() )
 val cars: List[Car] = List ( new Car(), new SportsCar() )

如您所见,List 不关心它是否包含动物或汽车。 List 的开发者并没有强制规定,例如只有汽车可以进入 List。

另外:

case class Shelter(animals: List[Animal]) {}

val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )

如果函数需要 List[Animal] 参数,您也可以将 List[Dog] 作为参数传递给函数。由于 List 的协方差,List[Dog] 被视为 List[Animal] 的子类。如果 List 不变的话,它就不起作用。

现在进入类型界限:

case class Barn[A <: Animal](animals: A*) {}

val animalBarn: Barn[Animal] = Barn( new Dog(), new Animal() )
val carBarn = Barn( new SportsCar() )
/* 
error: inferred type arguments [SportsCar] do not conform to method apply's type parameter bounds [A <: Animal]
    val carBarn = Barn(new SportsCar())
                 ^
*/

如您所见,Barn 是一个仅适用于动物的集合。这里不允许汽车进入。

I would like to extend Rex Kerr's excellent answer with some more examples:
Let's say we have four classes:

 class Animal {}
 class Dog extends Animal {}

 class Car {}
 class SportsCar extends Car {}

Let's start with variance:

 case class List[+B](elements: B*) {} // simplification; covariance like in original List

 val animals: List[Animal] = List( new Dog(), new Animal() )
 val cars: List[Car] = List ( new Car(), new SportsCar() )

As you can see List does not care whether it contains Animals or Cars. The developers of List did not enforce that e.g. only Cars can go inside Lists.

Additionally:

case class Shelter(animals: List[Animal]) {}

val animalShelter: Shelter = Shelter( List(new Animal()): List[Animal] )
val dogShelter: Shelter = Shelter( List(new Dog()): List[Dog] )

If a function expects a List[Animal] parameter you can also pass a List[Dog] as an argument to the function instead. List[Dog] is considered a subclass of List[Animal] due to the covariance of List. It would not work if List was invariant.

Now onto type bounds:

case class Barn[A <: Animal](animals: A*) {}

val animalBarn: Barn[Animal] = Barn( new Dog(), new Animal() )
val carBarn = Barn( new SportsCar() )
/* 
error: inferred type arguments [SportsCar] do not conform to method apply's type parameter bounds [A <: Animal]
    val carBarn = Barn(new SportsCar())
                 ^
*/

As you can see Barn is a collection only intended for Animals. No cars allowed in here.

海之角 2024-10-16 00:43:35

我在研究这个问题时发现了这篇博文。对 Scala 方差进行更深入的解释,包括其在类别理论中的理论基础

http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/

I found this blog post while researching this question. Gives an even deeper explanation of Scala variance including its theoretical basis in Category Theory

http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/

揽月 2024-10-16 00:43:35

根据我的理解:


第一个是参数类型界限,在我们的例子中,有一个上限和下限类型界限,它是“类型参数 A,它是 B(或 B 本身)的子类型。


第二个是类定义的方差注释,在我们的例子中,B


Scala 的协变子类化: + Java: ? extends T 协变子类化

Scala: - Java: ? super T 逆变子类化

for my Understanding:


The first is a parameter type bound, there a upper and lower typebounds in our case its a "type parameter A that is a subtype of B (or B itself).


The second is a Variance Annotation for a class defintion, in our case a covariance subclassing of B


Scala: + Java: ? extends T Covariant subclassing

Scala: - Java: ? super T Contravariant subclassing

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文