【问题标题】:Using wildcards in Java generics [duplicate]在 Java 泛型中使用通配符 [重复]
【发布时间】:2021-12-29 19:39:39
【问题描述】:

我正在使用 Y. Daniel Liang 的《Java 编程和数据结构简介》一书学习 Java 泛型,但其中一个练习我不懂:

"19.3(同一类型的对象对)创建一个封装两个对象的Pair类 在 Pair 的实例中具有相同的数据类型。

19.4(使用通配符)编写一个通用静态方法,它返回一个数组中的最小值 编程练习 19.3 中的 Pair 实例。"

解决方案代码如下:


public class Exercise19_04 {

    public static class Pair<T> {
        public final T one;
        public final T two;

        public Pair(T one, T two) {
            this.one = one;
            this.two = two;
        }
    }
    
    public static class Main {
        // To have the smallest between two objects, they need to be comparable.
        public static <T extends Comparable<? super T>> T smallest(Pair<T> p) {
            if (p.one.compareTo(p.two) < 0) {
                return p.one;
            } else {
                return p.two;
            }
        }
        
        public static void main(String[] args) {
            Pair<Double> pd = new Pair<>(7.0, 6.3);
            System.out.println(smallest(pd));
            Pair<String> ps = new Pair<>("big", "small");
            System.out.println(smallest(ps));
            /* Lines below are not compilable because Color is not comparable
            Pair<String> pc = new Pair<>(java.awt.Color.BLUE, java.awt.Color.RED);
            System.out.println(smallest(ps));*/
        }
    }
}

我的问题在于方法声明public static &lt;T extends Comparable&lt;? super T&gt;&gt; T smallest(Pair&lt;T&gt; p)

我知道,为了比较 Pair 实例中的对象 T,T 必须是 Comparable 的实例,因此您必须声明 &lt;T extends Comparable&gt;。但是我不明白之后的&lt;? super T&gt; 部分。有人可以向我解释一下吗?提前致谢!

【问题讨论】:

标签: java generics


【解决方案1】:

暂时忘记Pair。让我们看看Comparable。一开始可能会认为 Comparable 不需要参数。但这有一个问题。 compareTo(如a.compareTo(b))的签名是什么样的?

也许:

/**
 * Return negative if a is 'before' b, positive if a is 'after' b,
 * and 0 if they are equal in ranking.
 */
 public int compareTo(Object other);
}

但这是有问题的:现在例如Double 要实现 Comparable,它需要能够回答任何类型的问题。因此,如果我尝试将具有值 4.5 的 Double 与 InputStream 进行比较 - 这是一个无意义的问题。要求比较枪支和祖母。

因此,我们强烈希望签名是:

/**
 * Return negative if a is 'before' b, positive if a is 'after' b,
 * and 0 if they are equal in ranking.
 */
 public int compareTo(Double other);
}

但是怎么做呢?我们不能为现有的每种类型都创建一个新的Comparator 接口,并且在 java 中没有任何东西意味着“你自己的类型”。

泛型来拯救!让我们创建一个泛型参数来表示您可以自己比较的类型:

public interface Comparable<T> {
  public int compareTo(T other);

让我们用双精度实现它:

public class Double extends Number implements Comparable<Double> {
 ...

我们将Comparable 类型解读为:“Foos 的可比对象”,这意味着:可以与具有 Foo 类型的事物进行比较的事物。 (一般来说,我们只是指 Foos;当然,我们有兴趣将橙子与橙子进行比较)。

现在让我们把它放回Pair。我们不能写Pair&lt;Comparable&gt; 的原因与你不能写List&lt;List&gt; 的原因相同。是的,它会编译,但你会收到严重的警告:毕竟,好的列表列表。但是列表列表中的列表是什么?你会写List&lt;List&lt;String&gt;&gt;:字符串列表的列表。这同样适用于可比对象:它不仅仅是一对可比对象,它是一对 Foos 的可比对象。 Pair&lt;Comparable&lt;String&gt;&gt; 的意思是:我有两件东西,每件都是知道如何将自己与字符串进行比较的某种类型。 (而且,剧透警告,只有 java.lang.String 符合这个要求)。

这只会留下两个问题:

  1. 为什么是Pair&lt;? extends Comparable&lt;..&gt;&gt; 而不仅仅是Pair&lt;Comparable&gt;
  2. 为什么是Pair&lt;... Comparable&lt;? super T&gt;&gt; 而不仅仅是Comparable&lt;T&gt;

这些问题的答案:

  1. 因为 PECS。请参阅 cmets 中的链接答案。
  2. 这很少相关,但从理论上讲,如果您有某种类型的对象能够将自己与某些 SUPERtype 字符串进行比较(假设“可以将自己与任何类型的任何对象进行比较”),那么,那'd..会很好。当我喂它时,我们想要任何知道该怎么做的东西:嘿,把你自己和这个东西比较,这个东西是 T 型的。不管那可能是什么。如果这个东西可以将自己与某个超类型的 T 进行比较,那完全没问题。所有整数也是数字 - 所有 T 也是 T 的任何超类型。

【讨论】:

    猜你喜欢
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多