Почему java.lang.Number не реализует Comparable?
Кто-нибудь знает, почему java.lang.Number
не реализует Comparable
? Это означает, что вы не можете сортировать Number
с Collections.sort
, который мне кажется немного странным.
Обновление для обсуждения:
Спасибо за все полезные ответы. Я закончил работу еще несколько исследований по этой теме.
Простейшее объяснение того, почему java.lang.Number не реализует Comparable, основано на проблемах мутируемости.
Для небольшого обзора java.lang.Number
представляет собой абстрактный супертип AtomicInteger
, AtomicLong
, BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
и Short
. В этом списке AtomicInteger
и AtomicLong
не реализовать Comparable
.
Копаясь, я обнаружил, что не очень хорошая практика реализовать Comparable
в изменяемых типах, потому что объекты могут меняться во время или после сравнения, делая результат сравнения бесполезным. Оба AtomicLong
и AtomicInteger
изменяемы. Дизайнерам API было предусмотрительно не иметь Number
реализовать Comparable
, потому что это ограничило бы реализацию будущих подтипов. Действительно, AtomicLong
и AtomicInteger
были добавлены в Java 1.5 долго после того, как java.lang.Number
был первоначально реализован.
Помимо изменчивости, здесь, вероятно, есть и другие соображения. Реализация compareTo
в Number
должна была бы продвигать все числовые значения до BigDecimal
, поскольку она способна размещать все подтипы Number
. Последствия этого продвижения по математике и производительности немного неясны для меня, но моя интуиция находит, что решение kludgy.
Ответы
Ответ 1
Стоит отметить, что следующее выражение:
new Long(10).equals(new Integer(10))
всегда false
, который в какой-то момент стремится к обходу каждого. Таким образом, вы не можете не сравнивать произвольные Number
, но вы даже не можете определить, равны они или нет.
Кроме того, с реальными примитивными типами (float
, double
) определение того, являются ли два значения равными, является сложным и должно выполняться с допустимым пределом погрешности. Попробуйте использовать код:
double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
d2 += 0.1d;
}
System.out.println(d2 - d1);
и вы останетесь с небольшой разницей.
Итак, вернемся к вопросу создания Number
Comparable
. Как вы его реализуете? Использование чего-то вроде doubleValue()
не сделало бы это надежно. Помните, что подтипы Number
:
-
Byte
;
-
Short
;
-
Integer
;
-
Long
;
-
AtomicInteger
;
-
AtomicLong
;
-
float
;
-
double
;
-
BigInteger
; и
-
BigDecimal
.
Можете ли вы закодировать надежный метод compareTo()
, который не передается в ряд операторов ifofof? Number
экземпляры имеют только шесть доступных им методов:
-
byteValue()
;
-
shortValue()
;
-
intValue()
;
-
longValue()
;
-
floatValue()
; и
-
doubleValue()
.
Итак, я предполагаю, что Солнце ((разумное) решение, что Number
было только Comparable
для самих себя.
Ответ 2
Для ответа см. ошибку Java bugparade ошибка 4414323. Вы также можете найти обсуждение comp.lang.java.programmer
Чтобы процитировать ответ Sun на отчет об ошибке с 2001 года:
Все "цифры" не сопоставимы; сопоставимый предполагает полное упорядочение номера возможны. Это даже не true для чисел с плавающей запятой; NaN (а не число) не меньше, больше или равно значение с плавающей запятой, даже самого себя. {Float, Double}.compare наложить общий заказ отличается от заказа плавающей запятой "<" и "=" операторы. Кроме того, как в настоящее время реализованы подклассы Number сравнимы только с другими одного класса. Есть другие случаев, таких как комплексные числа, где нет существует стандартный общий заказ, хотя можно было бы определить. В короткий, независимо от того, является ли подкласс Число сопоставимо должно быть оставлено как решение для этого подкласса.
Ответ 3
чтобы реализовать сопоставимые по количеству, вам придется писать код для каждой пары подкласса. Это проще, чем просто позволить подклассам реализовать сопоставимые.
Ответ 4
Скорее всего, потому, что было бы довольно неэффективно сравнивать числа - единственное представление, в которое каждый Число может соответствовать, чтобы такое сравнение было бы BigDecimal.
Вместо этого неатомные подклассы Number реализуют само Сопоставимое.
Атомные являются изменчивыми, поэтому не могут реализовывать атомное сравнение.
Ответ 5
Вы можете использовать Transmorph для сравнения чисел с помощью класса NumberComparator.
NumberComparator numberComparator = new NumberComparator();
assertTrue(numberComparator.compare(12, 24) < 0);
assertTrue(numberComparator.compare((byte) 12, (long) 24) < 0);
assertTrue(numberComparator.compare((byte) 12, 24.0) < 0);
assertTrue(numberComparator.compare(25.0, 24.0) > 0);
assertTrue(numberComparator.compare((double) 25.0, (float) 24.0) > 0);
assertTrue(numberComparator.compare(new BigDecimal(25.0), (float) 24.0) > 0);
Ответ 6
Чтобы попытаться решить исходную проблему (сортировать список чисел), опция заключается в объявлении списка общего типа, расширяющего Number и реализующего Comparable.
Что-то вроде:
<N extends Number & Comparable<N>> void processNumbers(List<N> numbers) {
System.out.println("Unsorted: " + numbers);
Collections.sort(numbers);
System.out.println(" Sorted: " + numbers);
// ...
}
void processIntegers() {
processNumbers(Arrays.asList(7, 2, 5));
}
void processDoubles() {
processNumbers(Arrays.asList(7.1, 2.4, 5.2));
}
Ответ 7
нет сравнения stardard для чисел разных типов.
Однако вы можете написать свой собственный Comparator и использовать его для создания TreeMap < Number, Object > , TreeSet <Number> или Collections.sort(List <Number> , Comparator) или Arrays.sort(Number [], Comparator);
Ответ 8
Напишите свой собственный компаратор
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class NumberComparator implements Comparator {
@SuppressWarnings("unchecked")
@Override
public int compare(Number number1, Number number2) {
if (((Object) number2).getClass().equals(((Object) number1).getClass())) {
// both numbers are instances of the same type!
if (number1 instanceof Comparable) {
// and they implement the Comparable interface
return ((Comparable) number1).compareTo(number2);
}
}
// for all different Number types, let check there double values
if (number1.doubleValue() < number2.doubleValue())
return -1;
if (number1.doubleValue() > number2.doubleValue())
return 1;
return 0;
}
/**
* DEMO: How to compare apples and oranges.
*/
public static void main(String[] args) {
ArrayList listToSort = new ArrayList();
listToSort.add(new Long(10));
listToSort.add(new Integer(1));
listToSort.add(new Short((short) 14));
listToSort.add(new Byte((byte) 10));
listToSort.add(new Long(9));
listToSort.add(new AtomicLong(2));
listToSort.add(new Double(9.5));
listToSort.add(new Double(9.0));
listToSort.add(new Double(8.5));
listToSort.add(new AtomicInteger(2));
listToSort.add(new Long(11));
listToSort.add(new Float(9));
listToSort.add(new BigDecimal(3));
listToSort.add(new BigInteger("12"));
listToSort.add(new Long(8));
System.out.println("unsorted: " + listToSort);
Collections.sort(listToSort, new NumberComparator());
System.out.println("sorted: " + listToSort);
System.out.print("Classes: ");
for (Number number : listToSort) {
System.out.print(number.getClass().getSimpleName() + ", ");
}
}
}
Ответ 9
почему это была бы плохая идея?
abstract class ImmutableNumber extends Number implements Comparable {
// do NOT implement compareTo method; allowed because class is abstract
}
class Integer extends ImmutableNumber {
// implement compareTo here
}
class Long extends ImmutableNumber {
// implement compareTo here
}
другой вариант, возможно, заключался в объявлении класса Number реализует Comparable, опускает реализацию compareTo и реализует его в некоторых классах, таких как Integer, в то время как бросает UnsupportedException в других, таких как AtomicInteger.
Ответ 10
Моя догадка заключается в том, что, не внедряя Comparable, она дает больше гибкости для реализации классов для ее реализации или нет. Все общие числа (Integer, Long, Double и т.д.) Реализуют Comparable. Вы все равно можете вызвать Collections.sort, пока сами элементы реализуют Comparable.
Ответ 11
Взгляд на иерархию классов. Классы Wrapper, такие как Long, Integer и т.д., Реализуют Comparable, т.е. Целое число сопоставимо с целым числом, а long сравнимо с длинным, но вы не можете их смешивать. По крайней мере, с этой парадигмой дженериков. На мой вопрос отвечает вопрос "почему".
Ответ 12
byte
(примитив) является int
(примитивным). Примитивы имеют только одно значение за раз.
Правила создания языка позволяют это.
int i = 255
// down cast primitive
(byte) i == -1
A byte
не является Integer
. byte
- это Number
, а Integer
- Number
. Number
объекты могут иметь более одного значения в одно и то же время.
Integer iObject = new Integer(255);
System.out.println(iObject.intValue()); // 255
System.out.println(iObject.byteValue()); // -1
Если a byte
является Integer
, а Integer
является Number
, какое значение вы используете в методе compareTo(Number number1, Number number2)
?