Java 泛型中如何使用逆变?

发布于 2024-09-26 06:37:55 字数 390 浏览 4 评论 0原文

在 Java 中,协变允许 API 设计者指定可以将实例概括为某种类型或该类型的任何子类型。例如:

List<? extends Shape> shapes = new ArrayList<Circle>(); 
// where type Circle extends Shape

逆变则相反。它允许我们指定一个实例可以被泛化为某种类型或超类型。

List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry

Java 泛型的逆变有何用处?您什么时候会选择使用它?

In Java, covariance allows the API designer to specify that an instance may be generalised as a certain type or any of that type's subtypes. For example:

List<? extends Shape> shapes = new ArrayList<Circle>(); 
// where type Circle extends Shape

Contravariance goes the other way. It allows us to specify that an instance may be generalised as a certain type or supertype.

List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry

How is Java generic's contravariance useful? When would you choose to use it?

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

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

发布评论

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

评论(3

天生の放荡 2024-10-03 06:37:55

以下是 Java 泛型和集合 的相关摘录:

2.4。 Get 和 Put 原则

尽可能插入通配符可能是一个好习惯,但是您如何决定
使用哪个通配符?哪里应该使用extends,哪里应该使用super
哪些地方不适合使用通配符?

幸运的是,有一个简单的原则可以决定哪种方法是合适的。

获取和放置原则:使用
extends 通配符,当你只得到
结构之外的值,使用 super
当您只将值放入时通配符
结构体,并且不使用通配符
当你既获取又放置时。

我们已经在 copy 方法的签名中看到了这一原理:

public static <T> void copy(List<? super T> dest, List<? extends T> src)

该方法从源 src 中获取值,因此使用 extends 通配符声明它,
并将值放入目标 dst,因此它是用 super 通配符声明的。
每当您使用迭代器时,您都会从结构中获取值,因此请使用 extends
通配符。这是一个方法,它接受数字集合,将每个数字转换为双精度值,
并总结它们:

public static double sum(Collection<? extends Number> nums) {
    double s = 0.0;
    for (Number num : nums) s += num.doubleValue();
    return s;
}

Here's a relevant excerpt from Java Generics and Collections:

2.4. The Get and Put Principle

It may be good practice to insert wildcards whenever possible, but how do you decide
which wildcard to use? Where should you use extends, where should you use super,
and where is it inappropriate to use a wildcard at all?

Fortunately, a simple principle determines which is appropriate.

The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use a super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.

We already saw this principle at work in the signature of the copy method:

public static <T> void copy(List<? super T> dest, List<? extends T> src)

The method gets values out of the source src, so it is declared with an extends wildcard,
and it puts values into the destination dst, so it is declared with a super wildcard.
Whenever you use an iterator, you get values out of a structure, so use an extends
wildcard. Here is a method that takes a collection of numbers, converts each to a double,
and sums them up:

public static double sum(Collection<? extends Number> nums) {
    double s = 0.0;
    for (Number num : nums) s += num.doubleValue();
    return s;
}
笔芯 2024-10-03 06:37:55

好吧,你的第二个例子将允许你写:

Shape shape = getShapeFromSomewhere();
shapes.add(shape);

而你不能用第一个形式做到这一点。我承认,它不像协方差那么有用。

可以有用的一个领域是比较。例如,考虑一下:

class AreaComparer implements Comparator<Shape>
...

您可以使用它来比较任意两个形状...因此,如果我们可以使用它来对List进行排序,那就太好了。例如,圆>。幸运的是,我们可以通过逆变来做到这一点,这就是为什么 Collections.sort 存在重载:

public static <T> void sort(List<T> list, Comparator<? super T> c)

Well, your second example would allow you to write:

Shape shape = getShapeFromSomewhere();
shapes.add(shape);

whereas you couldn't do that with the first form. It's not useful as often as covariance, I'll grant you.

One area where it can be useful is in terms of comparisons. For example, consider:

class AreaComparer implements Comparator<Shape>
...

You can use that to compare any two shapes... so it would be nice if we could also use it to sort a List<Circle> for example. Fortunately, we can do that with contravariance, which is why there's an overload for Collections.sort of:

public static <T> void sort(List<T> list, Comparator<? super T> c)
甜尕妞 2024-10-03 06:37:55

例如,当实现 Collections.addAll()< /a> 方法,您需要一个可以包含某种类型 T 或 T 的超类型的集合。该方法如下所示:

public static <T> void addAll(Collection<? super T> collection, T... objects) {
    // Do something
}

For example, when implementing the Collections.addAll() method, you need a collection that can contain some type T or a supertype of T. The method then looks like:

public static <T> void addAll(Collection<? super T> collection, T... objects) {
    // Do something
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文