为什么要使用嵌套类来实现比较器?

4

我在浏览 Docjar 上的 String 相关文档时,偶然看到了下面这段代码:

public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();

private static class CaseInsensitiveComparator
                       implements Comparator<String>, java.io.Serializable {
    // use serialVersionUID from JDK 1.2.2 for interoperability
    private static final long serialVersionUID = 8575799808933029326L;

    public int compare(String s1, String s2) {
        // ...
    }
}

我的问题是,为什么我们不能像实现Comparable那样实现Comparator,并使用私有方法而不是嵌套类呢?

另外,顺便问一句,为什么Comparator没有一个类似于Comparable中的单参数方法compareTo呢?


1
你可以。这不是一个真正的问题。但是通常情况下,一个类实现自己的比较器方法是没有意义的。比较器是用于当你有两个“外部”对象时使用的。 - user207421
@EJP - 换句话说,你可以这样做...但这是一个愚蠢的想法。 - Stephen C
6个回答

6
因为String不是Comparator。当然,它们确实是Comparable,但它们本身不是“比较函数”:让String实现Comparator没有意义。
另一方面,CaseInsensitiveComparator是一个特定的比较函数,仅适用于字符串。因此,它被声明为一个static嵌套类。
1参见Comparator

2
我的问题是:我们为什么不能只实现一个比较器(comparator),就像comparable一样,而使用一个私有方法而不是内部类呢?
嗯,(基本上)Comparator接口的整个意义在于接口的实现是与被比较对象的类分开的独立类。
理论上你可以按照你的想法去做,但最终结果是违反直觉的。
public class MyKey implements Comparator<MyKey> {
    private String field;

    public boolean compare(MyKey m1, MyKey m2) {
        // We must ignore this.field!  We are comparing m1 and m2 ...
        return m1.field.compareTo(m2.field);
    }
}

MyKey[] keys = ...
Arrays.sort(keys, new MyKey());  // Note we have to pass an instance
                                 // to provide the Comparator.

除了有点违反直觉,你的想法还有一个局限性,即MyKey只能通过这种方式“提供”一个比较器。
坦率地说,如果你要这样做,让MyKey实现Comparable<MyKey>更加合理。
假设他们按照你的建议实现了String。那么这个……
   String[] strings = new String[]{"a", "c", "B"};
   Arrays.sort(strings);

... 意味着区分大小写排序,但是 ...

   String[] strings = new String[]{"a", "c", "B"};
   Arrays.sort(strings, "weasel");

...意味着不区分大小写进行排序。这个想法真的让你感到合理吗?真的吗?


1

您只能实现同一类型的一个接口。String 已经实现了 Comparable 接口,用于词典排序比较:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{

但它需要更多的compareTo方法(例如:执行不区分大小写的比较)。以下代码会导致编译错误:

public final class String
    implements java.io.Serializable, Comparable<String>, Comparable<String>, CharSequence
{

因此,这可能是它具有额外比较器的原因之一。
比较器的概念是一个对象,它提供两个项目之间的比较服务,而不是一个可与另一个进行比较的合同(可比接口)。
在字符串上实现比较器将编译,但这将是语义上错误的。
public final class String
    implements java.io.Serializable, Comparable<String>, Comparator<String>, CharSequence
{

我想我之前误解了,现在我明白你想说什么了。然而,从编译的角度来看,String 为什么不能同时实现 ComparatorComparable 是没有道理的。 - arshajii
1
同意,但在字符串上实现比较器在语义上是错误的(即意义/目的方面)。 - gerrytan

0
将其作为类而不是函数,使我们能够对集合(或其他可能执行比较的类)应用多态比较策略。这样,使用比较器的方法就不需要知道它是区分大小写的比较器、不区分大小写的比较器还是其他类型的比较器;它只需执行比较即可。但如果这是一个函数,这种多态性就不适用了。执行比较的方法要么必须使用“the”比较方法,要么必须知道正在执行哪种比较,以选择正确的方法。
与将第一个参数绑定到一个特定字符串相比,有两个参数可以让我们在任意数量的字符串上使用相同的比较器。否则,在处理大型集合时,我们将不得不跟踪大量的比较器。它还允许在左侧或右侧(或两者都是)使用子类来使用此比较器;如果只有一个参数,就没有这种灵活性。

0
主要原因是为了暴露Comparator对象,用于像TreeMap这样的东西,其中可以使用String作为键,或者其他需要排序或不同类型等式的对象。它不能是一个static方法,因为Comparator接口不能指定一个static方法,而且Comparator必须是实现该接口的对象,在像TreeMap这样的类中才能使用。我们需要一个单独的不区分大小写的比较器,因为默认比较(由Comparable的方法实现)已经被区分大小写的比较所占用。

0
为什么我们不能像实现comparable一样,实现comparator并使用私有函数而不是内部类呢? String类 实现了Comparable<String>接口,用于按字典顺序比较两个字符串,这是一种非常常见的比较方式。
然而,有时您需要以不同的方式比较两个字符串,例如在忽略大小写的情况下进行比较。Comparator 接口提供了一种以不同方式比较两个对象的方法。在这种情况下,实现了CaseInsensitiveComparator以提供此功能,并在String.compareToIgnoreCase()方法中使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接