以下是我的代码中的一个示例:
基类:
abstract class AbstractBase implements Comparable<AbstractBase> {
private int a;
private int b;
public int compareTo(AbstractBase other) {
// compare using a and b
}
}
实现:
class Impl extends AbstractBase {
private int c;
public int compareTo(Impl other) {
// compare using a, b and c with c having higher impact than b in AbstractBase
}
FindBugs将此报告为一个问题。但那是为什么呢?可能会发生什么?
我将如何正确实施解决方案?
答案 0 :(得分:13)
Impl#compareTo(Impl)
并未覆盖AbstractBase#compareTo(AbstractBase)
,因为他们没有相同的签名。换句话说,例如,在使用Collections#sort
时,它不会被调用。
答案 1 :(得分:4)
编辑:添加了无需投射的解决方案
如果您不想演员,可以尝试以下方式。
将您的基类改为:
abstract class AbstractBase<T extends AbstractBase<?>> implements Comparable<T> {
//...
public int compareTo(T other) {
//...
}
}
你和Impl上课:
class Impl extends AbstractBase<Impl> {
//...
@Override
public int compareTo(Impl other) {
//...
}
}
铸造解决方案:
一种可能的解决方案是覆盖Impl类中的compareTo(AbstractBase)方法,并显式检查是否传入了Impl的实例:
class Impl extends AbstractBase {
//...
@Override
public int compareTo(AbstractBase other) {
if (other instanceof Impl) {
int compC = Integer.compare(c, ((Impl) other).c);
if (compC == 0) {
return super.compareTo(other);
}
return compC;
}
return super.compareTo(other);
}
}
答案 2 :(得分:3)
以下是我尝试的内容。不完全确定这就是为什么findbugs会给出错误。
请参阅以下代码,并假设compareTo
方法实现。
比较相同的对象会产生不同的输出。
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints -2
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return (a + b) - (other.a + other.b);
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
public int compareTo(Impl other)
{
return super.compareTo(other) + (c - other.c);
}
}
在我的假设compareTo
的基础上,以下似乎是一个很好的解决方案。您可以尝试使用类似于getSum
的方法,该方法为对象实例提供值。
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints 0
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return getSum() - other.getSum();
}
public int getSum()
{
return a + b;
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
@Override
public int getSum()
{
return super.getSum() + c;
}
}
答案 3 :(得分:2)
正如sp00m所说,Impl#compareTo(Impl)
的签名与AbstractBase#compareTo(AbstractBase)
的签名不同,因此不会超载它。
关键在于理解为什么它不起作用,即使您尝试sort()
与另一个Impl
进行比较,其中更具体的签名 匹配。
在定义Comparable<AbstractBase>
时,您需要定义自己的方式
实例compareTo
AbstractBase
个实例。因此,您需要实施compareTo(AbstractBase)
。
您可以认为,Impl
是AbstractBase
的子类型,当两个Impl
之间进行比较时,将使用更具体的方法。问题是 Java具有静态绑定,因此编译器在编译时定义 哪个方法将用于解决每个方法调用。如果你正在做的是排序AbstractBase
,那么编译器将使用compareTo(AbstractBase)
,即AbstractBase
接口在实现Comparable(AbstractBase)
接口时定义的接口。
您可以Impl
实现Comparable<Impl>
接口以使用compareTo(Impl)
方法,但这只有在您明确排序已知为Impl
s的内容时才有效在编译时(即Impl
个对象或Collection<Impl>
)。
如果确实想要在您的两个对象Impl
s时应用不同的比较,那么您应该在Impl#compareTo(AbstractBase)
中进行某种双重调度,例如:
Impl >>>
int compareTo(AbstractBase other) {
return other.compareToImpl(this);
}
int compareToImpl(Impl other) {
// perform custom comparison between Impl's
}
AbstractBase >>>
int compareTo(AbstractBase other) {
// generic comparison
}
int compareToImpl(Impl other) {
// comparison between an AbstractBase and an Impl.
//Probably could just "return this.compareTo(other);", but check for loops :)
}
这需要您在Impl
中添加一些AbstractBase
信息,但这不是很好,但是可以更优雅的方式解决问题 - 使用反射不< / strong>优雅。
答案 4 :(得分:2)
Liskov替换原则(http://en.wikipedia.org/wiki/Liskov_substitution_principle)指出:如果S是T的子类型,则类型T的对象可以用类型S的对象替换(即,类型S的对象可以替换类型为T的对象) )不改变该程序的任何理想属性(正确性,执行任务等)
在您的情况下,您将以打破原始方法行为的方式覆盖Base类中的compareTo方法。这可能是FindBugs遇到问题的原因。
如果你想对此保持正确:
abstract class AbstractBase {
}
class Impl1 extends AbstractBase implements Comparable<Impl1> ...
class Impl2 extends AbstractBase implements Comparable<Impl2> ...
OR
更好的是,根本不要使用Comparable接口 - 而是在排序时使用Comparator。
然而,在现实生活中,有些情况下你需要绕过它(也许你无法访问AbstractBase的源代码,或者你的新类可能只是一个POC)。在这些特殊情况下,我会选择&#34; ugly&#34;约翰提出的演员解决方案。