Почему 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)?