如何比较Java中的类和继承的类

时间:2011-02-13 10:26:42

标签: java sorting compare

我有两个类 - Task(实现Comparable)和DeadlinedTask(DeadlinedTask扩展Task)。对于他们每个人,我编写了一个重载的compareTo函数(每个函数都有compareTo(Task)和compareTo(DeadlinedTask))。

我的想法是,我可以按类别排序普通任务,截止日期前截止DeadlinedTasks,但我也希望所有DeadlinedTasks都排在任务之上。

当我在一系列任务(没有DeadlinedTasks)上调用Collections.sort(myListOfTasks)时,一切都像魅力一样。 但是当我有一个Tasks和DeadlinedTasks的列表时,对象会改变顺序,但它们没有完全排序。

我已经尝试在interclass比较中返回1以外的数字(1,1000,1000000都做了同样的事情)。有没有办法通过compareTo和Collections.sort来做到这一点,我可以使用不同的java功能,还是我必须编写自己的搜索功能(作为比较器?)?

任务比较方法:

public int compareTo(Task other){
    if(this.GetCategory().compareTo(other.GetCategory())==0)
        return this.GetName().compareTo(other.GetName());
    else 
        return this.GetCategory().compareTo(other.GetCategory());
}
public int compareTo(DeadlinedTask other){
    return 1;
}

DeadlinedTask compareTo方法:

public int compareTo(Task other){
    return -1;
}
public int compareTo(DeadlinedTask other){
    if(this.GetDeadline().compareTo(other.GetDeadline())==0)
        return this.GetName().compareTo(other.GetName());
    else 
        return this.GetDeadline().compareTo(other.GetDeadline());
}

感谢您的帮助

5 个答案:

答案 0 :(得分:5)

  

...或者我是否必须编写自己的搜索功能(作为比较器?)?

是。我认为这是最好的方式。

处理equalscompareTo的正常方法是返回false(针对equals)或抛出ClassCastException(针对compareTo)如果参数实际类型与this的实际类型不匹配。

如果您尝试为子类型实施equalscompareTo,则可以轻松创建语义异常,例如:

  • a.equals(b)b.equals(a)返回不同的值,或
  • a.compareTo(b)b.compareTo(a)返回不一致的值。

避免这些异常将需要使超类型知道该子类型。从设计角度来看,这是一个糟糕的因素,因为它限制了您将来创建更多子类型的能力。

对于需要实现订购两个或更多不同类的实例的规则的用例,Comparator是最佳解决方案。

答案 1 :(得分:2)

每个类只能使用一个compareTo方法来实现Comparable接口。如果你在没有泛型的情况下使用Comparable,那么这就是

public int compareTo(Object o)

如果您使用的是泛型,例如Comparable<Task>,然后就是

public int compareTo(Task o)

compareTo(DeadlinedTask o)接口将忽略您的Comparable<Task>方法。它只是“意外”具有相同的名称,但它是一个独立的overloading

(顺便说一下,不可能实现Comparable<Task>Comparable<DeadlineTask>)。

所以你需要做的就是改变你的Task.compareTo(Task o)方法以使用instanceof(毕竟它必须使用运行时信息)。我同意斯蒂芬的观点,写一个比较器会更好。

答案 2 :(得分:1)

Comparable定义了类的所有实例的自然顺序。因此,如果DeadlinedTask总是应该在Tasks之前,那么compareTo方法应该实现它。

你不应该在DeadlinedTask中重新定义compareTo,因为这会破坏反交换的契约:if(t1.compareTo(t2) > 0),然后是t2.compareTo(t1) < 0

因此我完全避免在Task类中实现Comparable,并在排序任务集合时使用专用比较器。如果你真的希望你的任务实现Comparable,那么你需要让它的实现取决于DeadlinedTask的存在(这不是非常OO):

public class Task implements Comparable<Task> {
    // ...
    public final int compareTo(Task t) {
        if (this instanceof DeadlinedTask) {
            if (t instanceof DeadlinedTask) {
                return ((DeadlinedTask) this).getDeadline().compareTo(((DeadlinedTask) t).getDeadline());
            }
            else {
                return -1;
            }
        }
        else if (t instanceof DeadlinedTask) {
            return 1;
        }
        else {
            return this.category.compareTo(t.category);
        }
    }
}

请注意,Java在方法的开头使用小写字母(getDeadline(),而不是GetDeadline()),并且您不需要使用getter来访问自己的私有属性类。

答案 3 :(得分:1)

除了StevenC所说的,如果你事先知道你将拥有一个值对象的层次结构,你可以检查compareTo()方法的参数的类是否是对象类的子类型如果是,则反转比较,因此您将始终让孩子与父母进行比较:

public boolean compareTo(Object o) {
  // check for null
  boolean isSubtype = getClass().isAssignableFrom(o.getClass()) && getClass()!=o.getClass()
  if (isSubtype) return -((/*cast to this type*/) o).compareTo(this);
}

这样,比较保持一致,基本类型不知道每个单独的子类型,但只存在这些子类型。

答案 4 :(得分:0)

是的,似乎比较器是最简单的(也是最干净的方式) 但是你可以简单地将工作量委托给你已经编写的compareTo(...)方法,你真正需要添加的是处理子类和超类之间比较的代码:

public int Compare(Task t1, Task t2) {

    if (t1 instance of DeadlinedTask && !(t2 instanceof DeadlinedTask))
        return 1;
    else if (t2 instance of DeadlinedTask && !(t1 instanceof DeadlinedTask))
        return -1;
    else
        return t1.compareTo(t2);
}

但它刚刚发生,您如何宣布课程?你在Task类的implements子句中包含Comparable,反之亦然?如果没有,那么也许当lhs对象是一个Task时,那么只有比较(Task)被调用?否则你需要在implements子句中同时包含两个:

class Task implements Comparable<Task>, Comparable<DeadlinedTask>

返回值的大小不会改变任何东西,即返回1和1000000完全相同,因为测试只是&lt; 0,&gt; 0和== 0(此合约在Comparator界面的文档中指定。我曾经告诉学生试图记住返回值的含义,想象一下比较整数,然后我们可以写:

int compare (int a, int b) { return a - b; }