首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >下限通配符(Comparable<?超级K>)

下限通配符(Comparable<?超级K>)
EN

Stack Overflow用户
提问于 2019-02-14 06:02:22
回答 3查看 298关注 0票数 3

在集合中,经常使用强制转换为Comparable接口,例如。在PriorityQueue

代码语言:javascript
复制
private static <T> void siftUpComparable(int k, T x, Object[] es) {
    Comparable<? super T> key = (Comparable<? super T>) x;
    ...
        if (key.compareTo((T) e) >= 0)
            break;
    ...
}

比方说,我们创建了整数队列并添加了一些东西:

代码语言:javascript
复制
PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.add(1);

如果我正确理解了通配符的概念,那么使用<? super T>而不是<T>的唯一效果就是编译器扩展了compareTo可能的参数类型

代码语言:javascript
复制
public interface Comparable<T> {
    public int compareTo(T o);
}

添加到Integer的任何超类。但我想知道为什么以及如何在这里使用它。任何示例,其中

代码语言:javascript
复制
Comparable<T> key = (Comparable<T>) x;

还不够吗?或者这是一种使用“超级”通配符和可比的指导方针?

EN

回答 3

Stack Overflow用户

发布于 2019-02-27 00:13:00

放松泛型签名可以提高代码的灵活性,特别是当我们讨论参数类型时。一般的指导原则是PECS规则,但是对于Comparable来说,需要这种灵活性的实际例子很少,因为这意味着在它的所有子类型之间有一个定义自然顺序的基类型。

例如,如果你有像这样的方法

代码语言:javascript
复制
public static <T extends Comparable<T>> void sort(Collection<T> c) {

}

你不能这样做

代码语言:javascript
复制
List<LocalDate> list = new ArrayList<>();
sort(list); // does not compile

原因是LocalDate实现了ChronoLocalDate,您可以将ChronoLocalDate的所有实现相互比较,换句话说,它们都实现了Comparable<ChronoLocalDate>

因此,方法签名必须是

代码语言:javascript
复制
public static <T extends Comparable<? super T>> void sort(Collection<T> c) {

}

为了允许实际类型实现使用超级类型参数化的Comparable,就像声明了Collections.sort一样。

对于siftUpComparable的特定情况,即没有机会确定实际泛型签名的内部集合方法,它使用Comparable<T>还是Comparable<? super T>并不重要,因为它需要的是将T的实例传递给compare方法的能力,而即使提供实际参数也是由另一个未经检查的强制转换完成的。

实际上,由于类型转换是未检查的,所以它也可以转换为Comparable<Object>,这将消除在compare调用时进行(T)转换的需要。

但显然,作者并不打算偏离实际泛型代码的用途,而是使用相同的模式,即使它在这里没有明显的好处。

票数 3
EN

Stack Overflow用户

发布于 2019-02-14 06:33:18

E实际上可能不会为它自己的类公开一个可比较的。想象一下这样的情况:

代码语言:javascript
复制
class Foo implements Comparable<Foo> { ... }

class Bar extends Foo { ... }

如果将优先级队列设置为PriorityQueue<Bar>,则使用Foo中定义的比较方法。也就是说,你有一个通配符被实现为FooComparable<? super Bar>

票数 2
EN

Stack Overflow用户

发布于 2019-02-14 06:09:49

泛型的全部要点是类型安全。由于Java泛型在运行时会丢失类型信息,因此我们没有验证此类型转换的方法。有界通配符是在编译时验证类型限制的一种安全方法,而不是在运行时进行hail类型转换。

要了解泛型中superextends的不同语义,请参阅以下主题:

What is PECS (Producer Extends Consumer Super)?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54680144

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档