【问题标题】:Comparable and Comparator Interface in JavaJava 中的 Comparable 和 Comparator 接口
【发布时间】:2012-10-23 08:20:59
【问题描述】:

我想编写一个通用的 Pair 类,它有两个成员:键和值。该类的唯一要求是键和值都应实现 Comparable 接口,否则 Pair 类将不接受它们作为类型参数。
首先我这样编码:

public class Pair<T1 extends Comparable, T2 extends Comparable>

但是 JDK 1.6 编译器会对此产生警告:

Comparable is a raw type. References to generic type Comparable<T> should be parameterized

然后我尝试添加类型参数,代码现在看起来像这样:

public class Pair<T1 extends Comparable<? extends Object>,
                  T2 extends Comparable<? extends Object>>

现在一切顺利,直到我尝试为 Pair 生成一个比较器。(以下代码在 Pair 类中)

public final Comparator<Pair<T1, T2>> KEY_COMPARATOR = new Comparator<Pair<T1, T2>>() {
        public int compare(Pair<T1, T2> first, Pair<T1, T2> second) {
            *first.getKey().compareTo(second.getKey());*
            return 0;
        }
    };

代码first.getKey().compareTo(second.getKey()); 会产生错误提示:

The method compareTo(capture#1-of ? extends Object) in the type Comparable<capture#1-of ? extends Object> is not applicable for the  arguments (T1)

有人知道这个错误信息是什么意思吗?
欢迎提供有关此主题的任何提示。

更新:
完整代码如下:

public class Pair<T1 extends Comparable<? extends Object>, T2 extends Comparable<? extends Object>> {
    private T1 key;
    private T2 value;

    public static int ascending = 1;
    public final Comparator<Pair<T1, T2>> KEY_COMPARATOR = new Comparator<Pair<T1, T2>>() {
        public int compare(Pair<T1, T2> first, Pair<T1, T2> second) {
            int cmp = first.getKey().compareTo((T1)(second.getKey()));
            if (cmp > 0)  return ascending;
            return -ascending;
        }
    };
}

@MarvinLabs 你能解释一下为什么编译器不能确保将对象与相同类型的其他对象进行比较。上述代码中second.getKey()返回T1类型,与first.getKey()属于同一类型

【问题讨论】:

  • 如果您仍有问题,请查看我的编辑。如果不是,请接受对您有帮助的回答。

标签: java generics comparator comparable


【解决方案1】:

我会这样声明我的班级:

public class Pair<T1 extends Comparable<T1>, T2 extends Comparable<T2>> 

意味着对象可以与与其相同类型的对象进行比较(您的错误意味着编译器无法确保将对象与相同类型的其他对象进行比较)。


您的代码与我的编辑正确编译:

public class Pair<T1 extends Comparable<T1>, T2 extends Comparable<T2>> {
    private T1 key;
    private T2 value;

    public T1 getKey() {
        return key;
    }

    public T2 getValue() {
        return value;
    }

    public final Comparator<Pair<T1, T2>> KEY_COMPARATOR = new Comparator<Pair<T1, T2>>() {
        public int compare(Pair<T1, T2> first, Pair<T1, T2> second) {
            return first.getKey().compareTo(second.getKey());
        }
    };

    public static void test() {
        Pair<String, Integer> p1 = new Pair<String, Integer>();
        Pair<String, Integer> p2 = new Pair<String, Integer>();

        p1.KEY_COMPARATOR.compare(p1, p2);
    }
}

但是,您应该为比较器创建一个单独的类(或静态最终类),以便使用起来更直观,并且不会增加每个 Pair 实例的权重。

【讨论】:

  • 是的,这解决了问题,非常感谢。在我提供的代码中,即使我添加了类型转换,错误消息仍然存在:first.getKey().compareTo((T1)(second.getKey()))。类型转换应该通知编译器另一个对象是相同的类型。为什么它仍然给出错误信息?
  • 同样,我想你需要说明 T1 和 T2 是可比的。所以:new Comparator&lt;Pair&lt;T1 extends Comparable&lt;T1&gt;, T2 extends Comparable&lt;T2&gt;&gt;&gt;
  • 我更新了我的问题,详见完整代码(不长)。你能解释一下为什么编译器不能确保将对象与相同类型的其他对象进行比较吗?
  • @MarvinLabs,这里不需要演员表。
  • @cheng: 使用T1 extends Comparable&lt;? extends T1&gt; 毫无意义,因为Comparable 是一个消费者(它的方法接受T1 类型的参数),因此必须是super (记住PECS)(如果是extends,它的compareTo方法除了null之外什么都不能接受)。我们所关心的是T1 可以compareTo 另一个T1 -- 这就是T1 extends Comparable&lt;? super T1&gt; 所保证的 -- 只要它可以将任何super 带到T1,那么它可以使用T1
【解决方案2】:

让我们先来看看界面设计。

public interface Comparable<T> { 

   public int compareTo(T o);

} 

我们必须说这是非常典型的。因此,如果我们的类需要实现它,我们会这样做。

pubilc class ICanComparteWithMyself implements Comparable<ICanComparteWithMyself> { 

public int compareTo(ICanComparteWithMyselfo)    
   //code for compration
} 

当我们看到泛型参数类型时,确定我们将操作什么,所以对于泛型我们以相同的方式操作

public class ICanCompareMyGeneric<T> implements Comparable<T> {

   public int compareTo(T o)    
       //code for compration
    } 
}

在你的情况下,我们希望它确保泛型参数实现是自己的 Comparable,因为我们需要这样做

public class MyGenericCanCompareToItself<T extends Comparable<T>> { 

}

正如我们所见,这很常见。预期的(或没有的)限制是我们可以处理实现 Comparable 的类来为其自身类型。如果我们有

 public class ICanCompareStrings implements Comparable<String> {
      public int compareTo(String o)    
           //code for compration
      }
 }

所以对于MyGenericCanCompareToItself 类作为通用参数,我们可以使用public MyGenericCanCompareToItself 类,但不能使用ICanCompareStrings

编辑:

所以当我们现在涵盖了基础知识时,我们可以去解决你的问题

您的班级描述如下所示

public class Pair&lt;T1 extends Comparable&lt;? extends Object&gt;, T2 extends Comparable&lt;? extends Object&gt;&gt;

这没有太多感觉,因为与&lt;?&gt;不太一样

这个描述说:

我是一个 Pair 类,它使用两个通用参数,可以对我不知道的东西进行比较。

使用此代码,您无法在泛型参数不知道之前继续进行操作,然后在那里进行操作,最终得到类似的结果。

first.getKey.compareTo(null);

这就是为什么当你尝试强制转换时你的代码不编译,预期的类型是空的。


要改变这一点,您需要限定您的泛型参数应该具有可比性。

例如可以比较自身t

public class Pair&lt;T1 extends Comparable&lt;T1&gt;, T2 extends Comparable&lt;T2&gt;&gt;

这个描述说:

我是一个使用两个参数的 Pair 类,每个参数都可以与自身进行比较。

这就是您可能正在寻找的东西,另外它们可以在 T1 或 T2 的超类上进行比较

public class Pair&lt;T1 extends Comparable&lt;? super T1&gt;, T2 extends Comparable&lt;? super T2&gt;&gt;

这个描述说:

我是一个使用两个参数的 Pair 类,每个参数都可以与从它们传递的类进行比较。

我希望这可以帮助您了解泛型 ;-)。

【讨论】:

    【解决方案3】:

    如果您知道两者之间的区别:

    List<Object>
    

    List<String>
    

    ...这会更容易。基本上,它们是 java 中的两种不同类型(不是您想象的“相同” List 类型)。

    通用类型“逻辑”并不像您在脑海中想象的那样简单直接。

    我认为你应该将你的“Pair”类声明如下:

    public class Pair<T1 extends Comparable<T1>, T2 extends Comparable<T2>>
    

    既然您已经使“T1”T2”具有可比性,为什么还要实现 Comparator?

    如果您想使用“Collections.sort(myCollection, myComparator)”,那么您不必声明“T1”和“T2”是“可比较的”...只需确保您的“KEY_COMPARATOR”接受他们...

    无论哪种方式,代码中都存在冗余。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-22
      • 2021-03-17
      • 1970-01-01
      • 2011-01-17
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多