Почему java.lang.Number не реализует Comparable?[дубликат]

StackOverflow https://stackoverflow.com/questions/480632

  •  20-08-2019
  •  | 
  •  

Вопрос

На этот вопрос уже есть ответ здесь:

Кто-нибудь знает, почему 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 подтипы.Значение этого повышения с точки зрения математики и производительности мне немного неясно, но моя интуиция находит это решение неуклюжим.

Это было полезно?

Решение

Стоит отметить следующее выражение:

new Long(10).equals(new Integer(10))

всегда false, что в тот или иной момент имеет тенденцию сбивать всех с толку.Так что нельзя не только произвольно сравнивать Numbers, но вы даже не можете определить, равны они или нет.

Кроме того, с реальными примитивными типами (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() метод, который не превращается в серию операторов if instanceof? Number экземплярам доступно только шесть методов:

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue()
  • doubleValue().

Так что, я думаю, Сан приняла (разумное) решение, что Numberбыли только Comparable к примерам самих себя.

Другие советы

Ответ см. в разделе «Парад ошибок Java». ошибка 4414323.Вы также можете найти обсуждение на comp.lang.java.programmer

Цитируем ответ Sun на отчет об ошибке от 2001 года:

Все «цифры» несопоставимы;Сопоставимый предполагает, что общий упорядочение чисел возможна.Это даже не относится к числам с плавающей точкой;Нан (не число) не меньше, больше, чем больше, ни равным какому-либо значению с плавающей точкой, даже самим.{Float, Double} .compare навязывают общий упорядочение, отличное от упорядочения операторов с плавающей точкой "и" = ".Кроме того, как в настоящее время реализовано, подклассы числа сравнимы только с другими случаями того же класса.Существуют и другие случаи, такие как сложные числа, где нет стандартного общего общего упорядочения, хотя можно определить.Короче говоря, независимо от того, является ли подкласс числа сопоставим, следует оставить в качестве решения для этого подкласса.

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

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

Вместо этого неатомарные подклассы Number реализуют сам Comparable.

Атомарные изменяемы, поэтому невозможно реализовать атомарное сравнение.

Вы можете использовать Трансморф для сравнения чисел с помощью класса 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);

Чтобы попытаться решить исходную проблему (отсортировать список чисел), можно объявить список общего типа, расширяющего 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));
}

нет стандартного сравнения чисел разных типов.Однако вы можете написать свой собственный компаратор и использовать его для создания TreeMap<Number, Object>, TreeSet<Number> или Collections.sort(List<Number>, Comparator) или Arrays.sort(Number[], Comparator);

Напишите свой собственный компаратор

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's 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() + ", ");
 }
    }
}

почему это было бы плохой идеей?:

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.

Я предполагаю, что отсутствие реализации Comparable дает больше гибкости реализации классов для его реализации или нет.Все распространенные числа (целые, длинные, двойные и т. д.) реализуют Comparable.Вы по-прежнему можете вызывать Collections.sort, если сами элементы реализуют Comparable.

Смотрим на иерархию классов.Классы-оболочки, такие как Long, Integer и т. д., реализуют Comparable, т.е.Целое число сравнимо с целым числом, а длинное — с длинным, но их нельзя смешивать.По крайней мере, с этой парадигмой дженериков.Что, я думаю, отвечает на ваш вопрос «почему».

byte (примитивный) – это int (примитивный).Примитивы имеют только одно значение одновременно.
Правила языкового дизайна позволяют это.

int i = 255

// down cast primitive
(byte) i == -1

А Byte не является Integer. Byte это Number и Integer это Number. Number объекты могут иметь более одного значения одновременно.

Integer iObject = new Integer(255);
System.out.println(iObject.intValue());   // 255
System.out.println(iObject.byteValue());  // -1

Если Byte является Integer и Integer это Number, Какое значение вы будете использовать в compareTo(Number number1, Number number2) метод?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top