Как сравнить классы и унаследованные классы в Java - PullRequest
4 голосов
/ 13 февраля 2011

У меня есть два класса - Task (который реализует Comparable) и DeadlinedTask (где DeadlinedTask расширяет Task). И для каждого из них я написал перегруженную функцию CompareTo (у каждого есть CompareTo (Task) и CompareTo (DeadlinedTask)).

Идея состоит в том, что я могу сортировать обычные Задачи по категориям и DeadlinedTasks по крайнему сроку, но я также хочу, чтобы все DeadlinedTasks были отсортированы выше Задач.

Когда я вызываю Collections.sort (myListOfTasks) для списка только задач (без DeadlinedTasks), все работает как шарм. Однако, когда у меня есть список задач и 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 сравнить методы:

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 ]

5 голосов
/ 13 февраля 2011

... или мне нужно написать собственную функцию поиска (в качестве компаратора?)?

Да. Я думаю, что это лучший способ.

Обычный способ обработки equals и compareTo - вернуть false (для equals) или выбросить ClassCastException (для compareTo), если фактический тип аргументов не соответствует фактическому типу this.

Если вы попытаетесь реализовать equals или compareTo для подтипов, вы можете легко создать семантические аномалии, такие как:

  • a.equals(b) и b.equals(a) возвращают различные значения или
  • a.compareTo(b) и b.compareTo(a) возвращают противоречивые значения.

Предотвращение этих аномалий повлекло бы за собой информирование супертипа о подтипе. Это плохо с точки зрения дизайна, поскольку ограничивает вашу способность создавать больше подтипов в будущем.

Для случаев использования, когда требуется для реализации правила, которое упорядочивает экземпляры двух или более разных классов, Comparator является лучшим решением.

2 голосов
/ 13 февраля 2011

Для каждого класса для реализации интерфейса Comparable можно использовать только один метод compareTo.Если вы используете Comparable без генериков, то это

public int compareTo(Object o)

Если вы используете генерики, например, Comparable<Task>, то это

public int compareTo(Task o)

Ваш метод compareTo(DeadlinedTask o) будет игнорироватьсяотносительно интерфейса Comparable<Task>.Просто «случайно» имеет то же имя, но это независимая перегрузка .

(Кстати, невозможно реализовать и Comparable<Task>, и Comparable<DeadlineTask>).

Так что вместо этого вам придется изменить Task.compareTo(Task o) метод на instanceof (в конце концов, он должен использовать информацию времени выполнения).Я согласен со Стивеном, что было бы лучше написать компаратор.

1 голос
/ 13 февраля 2011

В дополнение к тому, что сказал 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);
}

Таким образом, сравнение остается непротиворечивым, и базовый тип не учитывает каждый отдельный подтип,но существуют только эти подтипы.

1 голос
/ 13 февраля 2011

Comparable определяет естественный порядок для всех экземпляров класса.Так что если DeadlinedTask всегда должен предшествовать Задачам, то метод CompareTo должен его реализовать.

Вы не должны переопределять CompareTo в DeadlinedTask, потому что это нарушит контракт антикоммутативности: если (t1.compareTo(t2) > 0), то t2.compareTo(t1) < 0.

Я бы, таким образом, полностью избегал реализацииСравним в классе Task и использует выделенный компаратор при сортировке набора задач.Если вы действительно хотите, чтобы ваша задача реализовала 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()), и что вам не нужно использовать геттеры для доступа к закрытым свойствам вашего собственного класса.

0 голосов
/ 13 февраля 2011

Да, кажется, что компаратор является самым простым (и самым чистым способом), но вы можете просто делегировать большую часть работы уже написанным методам сравнения (все, что вам действительно нужно добавить, это код для обработки).сравнение между подклассом и суперклассами:

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 и наоборот?если нет, то, возможно, когда объект lhs является Задачей, тогда вызывается только сравнение (Задача) ??в противном случае вам нужно иметь оба в предложении Implements, т.е.:

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

Величина возвращаемого значения ничего не изменит, то есть возвращение 1 и 1000000 абсолютно одинаково, так как тесты только <0,>0 и == 0 (этот контракт указан в документации для интерфейса Comparator. Раньше я говорил студентам, пытающимся вспомнить, что означают возвращаемые значения, чтобы представить сравнение целых чисел, тогда мы могли просто написать:

int compare (int a, int b) { return a - b; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...