【问题标题】:Comparing Java subclasses比较 Java 子类
【发布时间】:2012-05-24 02:55:11
【问题描述】:

我的应用可能会比较同一抽象父类的任意两个子类的实例。我希望它们进行如下比较:

  1. 如果它们是不同的子类,则应由父类进行比较。
  2. 如果它们是同一个子类,则子类应该进行比较。

类将通过 TreeMap 进行比较,因此我可以选择使用 Comparator 或实现 Comparable(或两者兼而有之?)。

我可以想到几种方法来做到这一点,但它们都有些混乱且容易出错。有没有优雅的解决方案?

提前谢谢...

【问题讨论】:

  • “类将通过 Treemap 进行比较”是什么意思?
  • 既然提到了 Comparable 和 Comparator,我们是否可以假设比较的结果具有所有三种可能性——“小于”、“大于”和“等于”?如果比较甚至排除了这些选项之一的可能性,那么您不应该使用这些接口。
  • 你的意思是通过反射比较类吗?比如每个类有哪些方法和属性?
  • 这是一个非常困难的任务,因为 compare() 的约定强加了传递性:如果 b1 > b2(与 B 子类比较),如果 a > b1(与基类比较),那么你必须有 a > b2 (与基类比较)。鉴于基类比较对子类比较一无所知,这很难实现。
  • @JBNizet - 虽然我认为 OP 的想法不一定是一个伟大的想法,但只要您的子类排序始终是“子比较”,我认为保持传递性并不困难的超类排序。

标签: java


【解决方案1】:

你可以试试

// Parent:
@Override
public final int compareTo(Parent other)
{
  if (getClass() == other.getClasss()) {
    // same type -> pass it to subclass implementation
    return this.subCompare(other)
  }

  // different type -> do the comparison here based on Parent's logic
  // ...
}

protected int subCompare(Parent other)
{
  // this should not be called directly
  return 0; // could throw an exception here too
}

// Derived1:
@Override
protected int subCompare(Parent other)
{
  // this method is only called from Parent
  Derived1 other1 = (Derived1) other;
  // do the comparison based on Derived1's logic
}

其他派生类也是如此

【讨论】:

  • 可能会使父 compare() 最终避免意外覆盖。否则,只要不需要在层次结构中的任意深度做出决定,这种方法就可以工作。
  • 仅供参考。 “this instanceof other.getClasss()”不是 Java 中的有效语句...
  • 是的,你只想要“getClass() == other.getClass()”
  • 谢谢。这与我已经在做的事情很接近,并且可能会变得很优雅。除了我使用带有 Comparator 和 Comparable 的混合解决方案:Comparator 检查它们是否是不同的子类,如果是,则处理比较。否则,它会调用 subclass.compareTo()。
  • @BarryFruitman 您可以通过首先根据基类规则进行比较来改进保证,并且只有当它们相等时,然后通过进行更具体的比较来打破平局基于子类。
【解决方案2】:

这不是您问题的直接答案,而是:
我相信您所做的事情容易出错且存在问题。
compareTo 方法施加的相等性测试通常应该返回与equals 方法相同的结果。 IE。 compareTo 方法强加的排序应该equals一致。
如果违反本合同,您可能会遇到排序集合(您正在使用)的问题。
我为什么这么说:
在某些情况下您希望将比较委托给父类这一事实表明您已在子类中添加了一个值组件。
如果您这样做了,则无法保留 equals 合同,并且您可能会遇到排序容器的问题(见上文)

【讨论】:

    【解决方案3】:

    无论哪种方式都容易出错。你可以在你的子类中做这样的事情:

    类子类1 ... {

    public boolean equals(Object o) {
      if(o instanceof Subclass1) {
        return super.equals(o);
      }
    
      ... compare subclasses
    }
    

    }

    【讨论】:

    • 我需要比较,而不是相等性检查。
    【解决方案4】:

    类层次结构是否应该可扩展?
    => 如果它不可扩展(或很少会扩展),你可以实现一个 Comperator 以便所有比较代码都在一个地方。

    排序对象的方法不止一种吗? => 如果是这样,那么您将不得不为每个订单使用一个比较器。

    compareTo(other)(或compare(o1, o2))从三种情况调用:

    1. this instanceof other.class(即o1 instanceof o2.class):
      • this.class 应该比较 thiso2,因为它比 other.class 包含更多信息;
    2. other instanceof this.class(即o2 instanceof o1.class):
      • other.class 应该比较 otherthis 并且 this.class.compareTo 应该翻转结果并返回它。
    3. 存在第一个共同祖先 P 使得this instanceof P && other instanceof P:
      • P 应该比较 thisother:递归调用 super.compareTo(other) 并返回结果。

    【讨论】:

    • 这是我在问题中提到的“混乱”解决方案之一。 Comparator 必须知道并能够比较子类的任何组合,这是不可能的,因为同一子类的实例可能需要使用私有成员相互比较。
    • 为了避免 Comperator 必须了解所有子类组合,Comperator 可以使用与 Comparable 对象类似的层次结构来实现。当然,这对所需的私人访问没有帮助。
    • 是的,我不认为 Comparator 是去这里的方式。认为你想坚持使用 Comparable impl。
    • 请将 Attila 的解决方案与我的比较。马铃薯/Potahto IMO。
    【解决方案5】:

    这就是我现在的做法。我认为这将变得非常优雅:

    public abstract class ParentClass implements Comparable<ParentClass> {
        // compareTo(ParentClass) is implicitly abstract
    }
    
    
    public class SubClass1 extends ParentClass /* Comparable<> is implicitly implemented */ {
        @Override
        public int compareTo(ParentClass another) {
            SubClass1 subAnother = (SubClass1) another;
            return /* result of sub-class comparison */;
        }
    }
    
    
    public class MyComparator implements Comparator<ParentClass> {
        @Override
        public int compare(ParentClass lhs, ParentClass rhs) {
    
            // Are lhs and rhs instances of the same sub-class?
            if(!lhs.getClass().equals(rhs.getClass()))
                // They are different. Use parent class comparison.
                return /* result of parent class comparison */;
    
            // They are the same. Use sub-class comparison.
            return lhs.compareTo(rhs);
        }
    }
    

    然后我只是将MyComparator 的一个实例传递给TreeMap。它处理不同子类的比较,如果它们相同,则将比较传递给子类。

    如果another 是另一个子类的实例,我不喜欢SubClass1.compareTo() 会抛出ClassCastException。不过我不必担心这一点,只要我确保子类仅通过MyComparator 进行比较。

    【讨论】:

    • 您可以添加一个只有 MyComperator 可以访问的方法,将 compareTo 的当前实现移到那里,然后在 compareTo 中调用 MyComperator.compare。
    • @KaspervandenBerg:我更喜欢将 compareTo() 保留在子类中,以便它可以访问私有成员。
    • 好的,我想出了如何避免 ClassCastException:Comparable 不需要实现,因为 compareTo() 只由 MyComparator 调用。相反,我只是将 compareTo() 设置为 ParentClass 中一个普通的旧抽象受保护方法。现在我可以 100% 确定 compareTo() 只会被相同的子类实例调用。
    • 这就是我想说的 ;)
    猜你喜欢
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 2011-09-03
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    相关资源
    最近更新 更多