Почему не java.ленг.Количество реализации сопоставимо? [дубликат]
этот вопрос уже есть ответ здесь:
- сравнение значений двух общих чисел 11 ответов
кто-нибудь знает, почему 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
, который, как правило, спотыкается все в тот или иной момент. Так что не только вы не можете сравнить произвольные Number
s, но вы даже не можете определить, равны они или нет.
кроме того, с реальными примитивными типами (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 принял (разумное) решение, что Number
s были только 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
напишите свой собственный компаратор
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)
способ?