Почему не java.ленг.Количество реализации сопоставимо? [дубликат]

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

кто-нибудь знает, почему java.lang.Number не реализует Comparable? Это означает, что вы не можете сортировать NumberС Collections.sort что мне кажется немного странным.

обсуждение update:

Спасибо за все полезные ответы. Я закончил делать еще несколько исследований по этой теме.

самое простое объяснение, почему java.ленг.Число не реализует сопоставимый коренится в проблемах изменчивости.

для небольшого обзора,java.lang.Number является абстрактным супертипом AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long и Short. В этом списке, AtomicInteger и AtomicLong не реализовывать Comparable.

рытье вокруг, я обнаружил, что это не хорошая практика, чтобы реализовать Comparable на изменяемых типах, потому что объекты могут меняться во время или после сравнения, делая результат сравнения бесполезным. Оба!--6--> и AtomicInteger изменяемы. У разработчиков API была предусмотрительность, чтобы не иметь Number реализовать Comparable потому что это ограничило бы реализацию будущих подтипов. Действительно,AtomicLong и AtomicInteger добавленные в Java 1.5 долго после java.lang.Number был первоначально реализован.

помимо изменчивости, здесь, вероятно, есть и другие соображения. А compareTo реализации в Number будет поощрять всех числовых значений BigDecimal потому что он способен вместить все Number суб-типы. Смысл этого продвижения с точки зрения математики и производительности мне немного неясен, но моя интуиция находит это решение kludgy.

12 ответов


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

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().

поэтому я думаю, что Sun принял (разумное) решение, что Numbers были только Comparable в случаях, когда они.


ответ см. В разделе Java bugparade 4414323 ошибка. Вы также можете найти Обсуждение от comp.ленг.Ява.программист!--2-->

цитата из ответа Sun на сообщение об ошибке от 2001 года:

все "числа" не сопоставимы; comparable предполагает общий порядок числа возможны. Это даже не true для чисел с плавающей запятой; NaN (не число) не меньше, чем, больше, чем любой другой значение с плавающей запятой, даже само по себе. {Float, Double}.сравнить наложить итог заказ отличается от заказа плавающей точки "


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


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

вместо этого неатомные подклассы числа реализуют сопоставимые себя.

атомарные изменчивы, поэтому не могут реализовать атомарное сравнение.


можно использовать найти все темы царей для сравнения чисел, используя свой класс 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);

чтобы попытаться решить исходную проблему (отсортировать список чисел), можно объявить список универсального типа, расширяющего число и реализующего сопоставимое.

что-то типа:

<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));
}

нет сравнения stardard для чисел разных типов. Однако вы можете написать свой собственный компаратор и использовать его для создания TreeMap, TreeSet или коллекций.сортировка (список, компаратор) или массивы.сортировка (число [], компаратор);


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

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
}

другой вариант может заключаться в том, чтобы объявить число классов реализует Compareto, опустить реализацию compareTo и реализовать его в некоторых классах, таких как Integer, а throw UnsupportedException в других, таких как AtomicInteger.


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


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


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

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