【问题标题】:How to compare Classes and Inherited Classes in Java如何比较 Java 中的类和继承类
【发布时间】:2011-02-13 10:26:42
【问题描述】:

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

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

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

我尝试在类间比较中返回 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());
}

感谢您的帮助

【问题讨论】:

    标签: java sorting compare


    【解决方案1】:

    ...还是我必须编写自己的搜索函数(作为比较器?)?

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

    处理equalscompareTo 的正常方法是如果参数实际类型与实际类型不匹配,则返回false(对于equals)或抛出ClassCastException(对于compareTothis.

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

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

    避免这些异常需要让超类型意识到子类型。从设计的角度来看,这很糟糕,因为它限制了您在未来创建更多子类型的能力。

    对于您需要实现对两个或多个不同类的实例进行排序的规则的用例,Comparator 是最佳解决方案。

    【讨论】:

      【解决方案2】:

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

      public int compareTo(Object o)
      

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

      public int compareTo(Task o)
      

      关于Comparable<Task> 接口,您的compareTo(DeadlinedTask o) 方法将被忽略。只是“不小心”同名,却是一个独立的overloading

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

      因此,您必须改为将 Task.compareTo(Task o) 方法更改为使用 instanceof(毕竟它必须使用运行时信息)。我同意 Stephen 的观点,写一个 Comparator 会更好。

      【讨论】:

        【解决方案3】:

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

        你不应该在 DeadlinedTask 中重新定义 compareTo,因为这会破坏反交换性的契约:if (t1.compareTo(t2) > 0), then 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 来访问您自己的类的私有属性。

        【讨论】:

        • 谢谢,还有其他我应该使用的标准命名方案吗?
        • 我认为在类中使用 get 和 set 方法的要点之一是,您可以更改“幕后”的工作方式,而无需修改任何其他代码。那么您不应该只有处理原始值的 get 和 set 方法,然后对变量的所有其他调用都将通过这些方法吗?
        【解决方案4】:

        除了 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);
        }
        

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

        【讨论】:

          【解决方案5】:

          是的,比较器似乎是最简单的(也是最干净的方式) 但是您可以简单地将大量工作委托给您已经编写的 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);
          }
          

          但它刚刚发生,你是如何声明类的?您是否将 Comparable 包含在 Task 类的 implements 子句中,反之亦然?如果不是,那么也许当 lhs 对象是一个任务时,只有 compare(Task) 被调用?否则,您需要在 implements 子句中同时包含两者,即:

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

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

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

          【讨论】:

          • 谢谢,在 Java 中使用 instanceof 有什么问题吗(来自 C++,我一直认为 typeof 是一种很老套的方法)。
          • 不,没有它有很多事情是不可能的。它有助于在 OOP 中拥有类型检查机制,尤其是严重依赖于 Java 等接口的语言(即单继承)
          猜你喜欢
          • 2016-02-07
          • 1970-01-01
          • 1970-01-01
          • 2021-12-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-26
          相关资源
          最近更新 更多