Какова наилучшая практика разделения логики в методах, которые будут работать с элементами Comparable, или конкретный Компаратор предоставляется во время создания, в зависимости от того, какой конструктор класса используется? Пример:
public class MinMax<E> {
private final Comparator<? super E> comparator;
private final boolean isSpecificComparatorProvided;
public MinMax() {
this.comparator = null;
this.isSpecificComparatorProvided = false;
........;
}
public MinMax(Comparator<? super E> comparator) {
this.comparator = comparator;
this.isSpecificComparatorProvided = true;
........;
}
private boolean isBigOrEqual(E e1, E e2) {
if (isSpecificComparatorProvided) {
Comparator<? super E> cpr = comparator;
int cmpr = cpr.compare(e1, e2);
return cmpr > -1;
} else {
@SuppressWarnings("unchecked")
Comparable<? super E> e1Comparable = (Comparable<? super E>) e1;
int cmprTo = e1Comparable.compareTo(e2);
return cmprTo > -1;
}
}
}
Всегда ли проверяю конкретный компаратор в методе isBigOrEqual()? Кроме того, применяется литье к Comparable, когда имеется NO конкретный компаратор? Есть ли другой способ?
Вероятно, самый простой способ справиться с этим - только реализовать случай для Comparator
, а затем, когда вы не передадите Comparator
, напишите тот, который работает только на Comparable
. (В Java 8 это просто Comparator.naturalOrder().)
new Comparator<T> {
@Override public int compare(T a, T b) {
return a.compareTo(b);
}
};
TreeMap
не делает этого по соображениям производительности, но проще это сделать.
Мне кажется, вы хотите абстрагировать способ сравнения двух объектов типа E и есть две реализации. Один использует конкретный Comparator
, а другой - для объектов Comparable
. Таким образом, вы можете разделить эти два поведения на отдельные классы (реализации).
Начнем с интерфейса для нашей абстрактной концепции:
interface AbstractComparer<E> {
public boolean isBigOrEqual(E e1, E e2);
}
Извините за неназванное имя.
Затем давайте реализовать случай, который касается объектов Comparable:
class ComparableComparer<E extends Comparable<E>> implements AbstractComparer<E> {
@Override
public boolean isBigOrEqual(E e1, E e2) {
return e1.compareTo(e2) > -1;
}
}
Реализация для другого случая получает конкретный компаратор через конструктор:
class ComparatorComparer<E> implements AbstractComparer<E> {
private Comparator<E> comparator;
public ComparatorComparer(Comparator<E> comparator) {
this.comparator = comparator;
}
@Override
public boolean isBigOrEqual(E e1, E e2) {
return this.comparator.compare(e1, e2) > -1;
}
}
Теперь мы можем упростить класс MinMax
и мы можем предоставить метод сравнения через конструктор:
public class MinMax<E> {
private final AbstractComparer<E> comparer;
public MinMax(AbstractComparer<E> comparer) {
this.comparer = comparer;
}
private boolean isBigOrEqual(E e1, E e2) {
return comparer.isBigOrEqual(e1, e2);
}
}
Вы можете ограничить E
сопоставимым с public class MinMax<E extends Comparable<E>>
поэтому приведение не требуется больше, поскольку E
во время компиляции E
должно быть сопоставимым:
public class MinMax<E extends Comparable<E>> {
private final Comparator<? super E> comparator;
private final boolean isSpecificComparatorProvided;
public MinMax() {
this.comparator = null;
this.isSpecificComparatorProvided = false;
}
public MinMax(Comparator<? super E> comparator) {
this.comparator = comparator;
this.isSpecificComparatorProvided = true;
}
private boolean isBigOrEqual(E e1, E e2) {
if (this.isSpecificComparatorProvided) {
return this.comparator.compare(e1, e2) > -1;
} else {
return e1.compareTo(e2) > -1;
}
}
}
Comparable
объектов без указанногоComparator
. И вам не нужно везде свернуть свой код сcomparator == null
проверками. Но если TreeMap был реализован так, как я это сделал, то пользователю TreeMap всегда нужно будет предоставлять объект AbstractComparer, что будет довольно раздражающим. Таким образом, вам придется решить, какой подход лучше для ваших нужд.