Как реализовать общую функцию «max (Comparable a, Comparable b)» в Java?

Я пытаюсь написать общую максимальную функцию, которая принимает две Comparables.

До сих пор у меня есть

public static <T extends Comparable<?>> T max(T a, T b) {
    if (a == null) {
        if (b == null) return a;
        else return b;
    }
    if (b == null)
        return a;
    return a.compareTo(b) > 0 ? a : b;
}

Это не компилируется с

The method compareTo(capture#5-of ?) in the type Comparable<capture#5-of ?> is not applicable for the arguments (T)

Я думаю, что это говорит о том, что ? в Comparable<?> можно интерпретировать как один тип для параметра a и другой для параметра b, так что их нельзя сравнивать.

Как мне выбраться из этой ямы?


person Duncan McGregor    schedule 23.06.2011    source источник
comment
В блоке if(a==null) нет необходимости во вложенном предложении if. Сокращение блока до return b; дает тот же результат (null, когда b равно null, b в противном случае).   -  person benjamin    schedule 28.11.2014
comment
См. org.apache.commons.lang3.ObjectUtils.compare   -  person Hollis Waite    schedule 07.10.2017


Ответы (5)


Для достижения наилучших результатов вы должны использовать public static <T extends Comparable<? super T>> T max(T a, T b).

Проблема с <T extends Comparable<?>> заключается в том, что это говорит о том, что тип T сравним с некоторым типом, но вы не знаете, что это за тип. Конечно, здравый смысл подсказывает, что класс, реализующий Comparable, должен иметь возможность быть сравнимым, по крайней мере, с самим собой (т. е. иметь возможность сравнивать объекты своего собственного типа), но технически ничто не мешает классу A реализовать Comparable<B>, где А и Б не имеют ничего общего друг с другом. <T extends Comparable<T>> решает эту проблему.

Но есть тонкая проблема с этим. Предположим, класс X реализует Comparable<X>, и у меня есть класс Y, который расширяет X. Таким образом, класс Y автоматически реализует Comparable<X> путем наследования. Класс Y также не может реализовать Comparable<Y>, потому что класс не может дважды реализовать интерфейс с параметрами разных типов. На самом деле это не проблема, поскольку экземпляры Y являются экземплярами X, поэтому Y сопоставим со всеми экземплярами Y. Но проблема в том, что вы не можете использовать тип Y с вашей функцией <T extends Comparable<T>> T max(T a, T b), потому что Y не реализует Comparable<Y>. Границы слишком строгие. <T extends Comparable<? super T>> устраняет проблему, потому что достаточно, чтобы T был сопоставим с некоторым супертипом T (который включал бы все экземпляры T). Вспомните правило PECS — производитель extends, потребитель super — в этом случае Comparable является потребителем (он принимает объект для сравнения), поэтому super имеет смысл.

Это границы типа, используемые всеми функциями сортировки и упорядочения в библиотеке Java.

person newacct    schedule 24.06.2011

Вы получаете эту ошибку, потому что Comparable<?> в основном говорит, что это сопоставимо с чем-то без какой-либо конкретики. Вместо этого вы должны написать Comparable<T>, чтобы компилятор знал, что тип T сравним с самим собой.

person Malcolm    schedule 23.06.2011
comment
Спасибо - теперь решать настоящую проблему, заключавшуюся в портировании на Scala! - person Duncan McGregor; 23.06.2011

Отвечая на мой собственный вопрос из связанных ссылок, сгенерированных SO - это кажется тонкой копией Fun with Java generics , хотя я думаю Вы не можете винить меня за то, что я не нашел его, учитывая название!

Кажется, самое простое решение

public static <T extends Comparable<T>> T max(T a, T b) {
    if (a == null) {
        if (b == null) return a;
        else return b;
    }
    if (b == null)
        return a;
    return a.compareTo(b) > 0 ? a : b;
}
person Duncan McGregor    schedule 23.06.2011

Я написал для этого служебный класс. Возможно, вы найдете это полезным (библиотека с открытым исходным кодом):

http://softsmithy.sourceforge.net/lib/docs/api/org/softsmithy/lib/util/Comparables.html

Домашняя страница:

http://www.softsmithy.org

Скачать:

http://sourceforge.net/projects/softsmithy/files/softsmithy/

Мейвен:

<dependency>  
    <groupid>org.softsmithy.lib</groupid>  
    <artifactid>lib-core</artifactid>  
    <version>0.1</version>  
</dependency> 
person Puce    schedule 23.06.2011
comment
Хорошо, спасибо. Это то, что я ошеломлен, не является частью стандартной библиотеки, учитывая, сколько кода требуется и как легко ошибиться. - person Duncan McGregor; 23.06.2011
comment
Примечание. Я только что проверил исходный код: в настоящее время он не поддерживает нулевые значения: softsmithy.hg.sourceforge.net/hgweb/softsmithy/lib/main-golden/ - person Puce; 23.06.2011
comment
:-) Спасибо, что рассказали правду - я уверен, что мой не был бы, если бы я не имел дело с доменом, где многие даты могли быть не указаны. - person Duncan McGregor; 23.06.2011

Часто лучше получить уже реализованный собственный iso create. См. функцию Min/Max с двумя Comparable. Самый простой org.apache.commons.lang.ObjectUtils:

Comparable<C> a = ...;
Comparable<C> b = ...;
Comparable<C> min = ObjectUtils.min(a, b);
person Grigory Kislin    schedule 05.05.2019