我有两个类 - 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());
}
感谢您的帮助
答案 0 :(得分:5)
...或者我是否必须编写自己的搜索功能(作为比较器?)?
是。我认为这是最好的方式。
处理equals
和compareTo
的正常方法是返回false
(针对equals
)或抛出ClassCastException
(针对compareTo
)如果参数实际类型与this
的实际类型不匹配。
如果您尝试为子类型实施equals
或compareTo
,则可以轻松创建语义异常,例如:
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; }