Warum java.lang.Number nicht vergleichbar implementieren? [Duplikat]
-
20-08-2019 - |
Frage
Diese Frage bereits eine Antwort hier:
- Vergleicht man die Werte von zwei generischen Zahlen 12 Antworten
Weiß jemand, warum java.lang.Number
Comparable
nicht implementiert? Das bedeutet, dass Sie nicht Number
s mit Collections.sort
, die mir scheint ein wenig seltsam sortieren.
Post Diskussion Update:
Vielen Dank für all die hilfreichen Antworten. Ich landete .
Die einfachste Erklärung dafür, warum java.lang.Number Vergleichbare nicht implementiert ist in Veränderlichkeit betrifft verwurzelt.
Für ein bisschen Bewertung ist java.lang.Number
der abstrakte Supertyp AtomicInteger
, AtomicLong
, BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
und Short
. Auf dieser Liste, AtomicInteger
und AtomicLong
nicht implementieren Comparable
.
Graben um, entdeckte ich, dass es nicht eine gute Praxis ist Comparable
auf änderbare Typen zu implementieren, da die Objekte während oder nach dem Vergleich wodurch das Ergebnis des Vergleichs nutzlos ändern können. Sowohl AtomicLong
und AtomicInteger
sind wandelbar. Die API-Designer hatten die Vorbedacht nicht Number
Comparable
zu implementieren zu haben, weil sie die Umsetzung der künftigen Subtypen eingeschränkt hätte. Tatsächlich wurden AtomicLong
und AtomicInteger
hinzugefügt in Java 1.5 noch lange nach java.lang.Number
umgesetzt wurde zunächst.
Neben Veränderlichkeit gibt es wahrscheinlich andere Überlegungen auch hier. Eine compareTo
Implementierung in Number
müßte alle numerischen Werte fördern, um BigDecimal
, weil es die Unterbringung aller Number
Untertypen fähig ist. Die Implikation, dass die Förderung in Bezug auf Mathematik und Leistung ein wenig unklar ist für mich, aber meine Intuition findet diese Lösung kludgy.
Lösung
Es ist erwähnenswert, dass die folgende Gleichung:
new Long(10).equals(new Integer(10))
ist immer false
, die jeder stolpern neigt zu einem bestimmten Zeitpunkt oder ein anderes auf. So können Sie nicht nur nicht willkürlich Number
s vergleichen, aber man kann nicht einmal feststellen, ob sie ist gleich oder nicht.
Auch mit den wirklichen primitiven Typen (float
, double
), zu bestimmen, ob zwei Werte gleich sind heikel ist und innerhalb einer akzeptablen Fehlerspanne durchgeführt werden. Versuchen Sie Code wie:
double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
d2 += 0.1d;
}
System.out.println(d2 - d1);
und Sie werden mit einigen kleinen Unterschied gelassen werden.
zur Ausgabe Also zurück Number
Comparable
machen. Wie würden Sie es umsetzen? Mit so etwas wie doubleValue()
wäre es nicht zuverlässig tun. Denken Sie daran, die Number
Subtypen sind:
-
Byte
; -
Short
; -
Integer
; -
Long
; -
AtomicInteger
; -
AtomicLong
; -
Float
; -
Double
; -
BigInteger
; und -
BigDecimal
.
Könnten Sie eine zuverlässige compareTo()
Methode Code, der nicht in eine Reihe, wenn Instanceof Aussagen nicht zufallen? Number
Instanzen haben nur sechs Methoden zur Verfügung zu ihnen:
-
byteValue()
; -
shortValue()
; -
intValue()
; -
longValue()
; -
floatValue()
; und -
doubleValue()
.
Also ich denke, Sun die (vernünftige) Entscheidung getroffen, dass Number
s nur auf Instanzen von sich selbst Comparable
wurden.
Andere Tipps
Für die Antwort finden Sie unter Java bugparade Fehler 4.414.323 . Sie können auch eine Diskussion von comp finden. lang.java.programmer
Ein Zitat aus der Sun Antwort auf den Bug-Report von 2001:
Alle „Zahlen“ nicht vergleichbar sind; vergleichbar nimmt eine Gesamtbestellung von Zahlen möglich ist. Das ist nicht einmal gilt für Gleitkommazahlen; NaN (Keine Zahl) ist weder weniger als, größer, noch gleich irgendein Gleitkommawert, auch selbst. {Float, Double} .compare insgesamt verhängen Bestellung unterscheidet sich von der Bestellung die Floating-Point "<" und "=" Betreiber. Zusätzlich wird, wie zur Zeit implementiert, die Unterklassen der Anzahl nur vergleichbar sind mit anderen Instanzen der gleichen Klasse. Da sind andere Fälle, wie komplexe Zahlen, in denen keine Standard totale Ordnung vorhanden ist, obwohl man definiert werden kann. Im Kurz gesagt, ob eine Unterklasse von Zahl ist vergleichbar sollte gelassen werden, wie eine Entscheidung für diese Unterklasse.
, um vergleichbare auf Nummer zu implementieren, würden Sie Code für jede Unterklasse Paar schreiben. Sein statt einfacher, nur Unterklassen ermöglichen vergleichbar zu implementieren.
Sehr wahrscheinlich, weil es ziemlich ineffizient wäre Zahlen zu vergleichen -. Die einzige Darstellung, in denen jede Zahl paßt so einen Vergleich zu ermöglichen wäre BigDecimal
Stattdessen Nicht-Atomunterklassen der Anzahl implementiert Comparable selbst.
Atomic diejenigen sind wandelbar, so kann nicht ein Atom Vergleich implementieren.
Sie können mit Transmorph Zahlen vergleichen, seine NumberComparator Klasse.
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);
Um zu versuchen, das ursprüngliche Problem (sortiert eine Liste von Zahlen) zu lösen, ist eine Option, um die Liste eines generischen Typs zu deklarieren Nummer erstreckt und Umsetzung vergleichbar.
So etwas wie:
<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));
}
gibt es keinen stardard Vergleich zur Anzahl der verschiedenen Arten.
Allerdings können Sie Ihre eigenen Vergleicher schreiben und es verwenden, um eine TreeMap
Schreiben Sie Ihren eigenen Vergleicher
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() + ", ");
}
}
}
, warum dies hätte schlechte Idee? :
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
}
eine weitere Option gewesen sein kann Klasse Anzahl implementiert Vergleichbar zu erklären, lassen compareTo Implementierung und implementieren es in einigen Klassen wie Integer, während UnsupportedException in anderen wie Atomicinteger werfen.
Meine Vermutung ist, dass durch nicht Umsetzung Vergleichbar es mehr Flexibilität zu implementierenden Klassen geben, sie umzusetzen oder nicht. Alle gemeinsamen Zahlen (Integer, Long, Double usw.) implementieren vergleichbar. Sie können immer noch Collections.sort so lange nennen, wie die Elemente selbst Vergleichbare implementieren.
in der Klassenhierarchie suchen. Wrapper-Klassen wie Long, Integer usw. implementieren Comparable, das heißt eine ganze Zahl ist vergleichbar mit einer ganzen Zahl und eine lange ist vergleichbar mit einem langen, aber man kann sie nicht mischen. Zumindest mit diesem Generika Paradigma. Welche Ich denke, beantwortet Ihre Frage ‚Warum‘.
byte
(primitive) ist ein int
(primitive). Primitives hat nur einen Wert zu einem Zeitpunkt.
Sprache Design-Regeln dies erlaubt.
int i = 255
// down cast primitive
(byte) i == -1
Ein Byte
ist kein Integer
. Byte
ist ein Number
und ein Integer
ist ein Number
. Number
Objekte können mehr als einen Wert zur gleichen Zeit haben.
Integer iObject = new Integer(255);
System.out.println(iObject.intValue()); // 255
System.out.println(iObject.byteValue()); // -1
Wenn ein Byte
ist ein Integer
und ein Integer
ist ein Number
, die einen Wert finden Sie in der compareTo(Number number1, Number number2)
Methode verwenden?