Domanda

Questa domanda ha già una risposta qui:

Qualcuno sa perché java.lang.Number non implementa Comparable?Ciò significa che non è possibile ordinare Numberè con Collections.sort il che mi sembra un po' strano.

Aggiornamento della discussione post:

Grazie per tutte le risposte utiliho finito per fare qualche ulteriore ricerca su questo argomento.

La spiegazione più semplice del motivo per cui java.lang.Number non implementa Comparable è radicata in problemi di mutabilità.

Per un po' di recensione, java.lang.Number è il supertipo astratto di AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long E Short.In quella lista, AtomicInteger E AtomicLong da non implementare Comparable.

Scavando ho scoperto che non è una buona pratica da attuare Comparable su tipi mutabili perché gli oggetti possono cambiare durante o dopo il confronto rendendo inutile il risultato del confronto.Entrambi AtomicLong E AtomicInteger sono mutevoli.I progettisti dell'API hanno avuto la previdenza di non averlo Number strumento Comparable perché avrebbe limitato l'implementazione dei futuri sottotipi.Infatti, AtomicLong E AtomicInteger furono aggiunti in Java 1.5 molto tempo dopo java.lang.Number è stato inizialmente implementato.

A parte la mutevolezza, probabilmente ci sono anche altre considerazioni qui.UN compareTo implementazione in Number dovrebbe promuovere tutti i valori numerici a BigDecimal perché è capace di accogliere tutto Number sottotipi.Le implicazioni di quella promozione in termini di matematica e prestazioni non mi sono chiare, ma la mia intuizione trova che la soluzione sia goffa.

È stato utile?

Soluzione

Vale la pena ricordare che la seguente espressione:

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

è sempre false, che prima o poi tende a far inciampare tutti.Quindi non solo non puoi confrontare arbitrariamente Numbers ma non puoi nemmeno determinare se sono uguali o no.

Inoltre, con i tipi primitivi reali (float, double), determinare se due valori sono uguali è complicato e deve essere effettuato entro un margine di errore accettabile.Prova un codice come:

double d1 = 1.0d;
double d2 = 0.0d;
for (int i=0; i<10; i++) {
  d2 += 0.1d;
}
System.out.println(d2 - d1);

e ti rimarrà qualche piccola differenza.

Torniamo quindi alla questione del fare Number Comparable.Come lo implementeresti?Usando qualcosa come doubleValue() non lo farei in modo affidabile.Ricorda il Number i sottotipi sono:

  • Byte;
  • Short;
  • Integer;
  • Long;
  • AtomicInteger;
  • AtomicLong;
  • Float;
  • Double;
  • BigInteger;E
  • BigDecimal.

Potresti codificare un file affidabile compareTo() metodo che non si trasforma in una serie di istanze if di istruzioni? Number le istanze hanno a disposizione solo sei metodi:

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

Quindi immagino che Sun abbia preso la (ragionevole) decisione NumberErano solo Comparable alle istanze di se stessi.

Altri suggerimenti

Per la risposta, consultare Bugparade Java bug 4414323 . Puoi anche trovare una discussione da comp. lang.java.programmer

Per citare la risposta di Sun alla segnalazione di bug del 2001:

  

Tutti " numeri " non sono comparabili;   comparabile presuppone un ordinamento totale di   i numeri sono possibili. Questo non è nemmeno   vero per i numeri in virgola mobile; NaN   (non un numero) non è né inferiore a,   maggiore di, né uguale a nessuno   valore in virgola mobile, anche se stesso.   {Float, Double} .compare impone un totale   ordinamento diverso dall'ordinamento   del virgola mobile " < " e " = "   operatori. Inoltre, come attualmente   implementato, le sottoclassi di Number   sono paragonabili solo ad altri casi   della stessa classe. Ci sono altri   casi, come numeri complessi, dove no   esiste un ordinamento totale standard,   sebbene uno potrebbe essere definito. Nel   in breve, indipendentemente dal fatto che una sottoclasse di   Il numero è comparabile dovrebbe essere lasciato come   una decisione per quella sottoclasse.

per implementare un numero comparabile, dovresti scrivere il codice per ogni coppia di sottoclassi. È invece più semplice consentire a sottoclassi di implementare comparabili.

Molto probabilmente perché sarebbe piuttosto inefficiente confrontare i numeri - l'unica rappresentazione in cui ogni numero può adattarsi per consentire tale confronto sarebbe BigDecimal.

Invece, le sottoclassi non atomiche di Number implementano lo stesso Comparable.

Quelli atomici sono mutabili, quindi non possono implementare un confronto atomico.

Puoi usare Transmorph per confrontare i numeri usando la sua classe 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);

Per provare a risolvere il problema originale (ordinare un elenco di numeri), un'opzione è dichiarare l'elenco di un tipo generico estendendo Numero e implementando Comparable.

Qualcosa del tipo:

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

non esiste un confronto stardard per numeri di tipi diversi. Tuttavia puoi scrivere il tuo comparatore e usarlo per creare una TreeMap & Lt; Number, Object & Gt ;, TreeSet & Lt; Number & Gt; oppure Collections.sort (Elenco < Numero > ;, Comparatore) o Arrays.sort (Numero [], Comparatore);

Scrivi il tuo comparatore

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

perché questa sarebbe stata una cattiva idea? :

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
}

Un'altra opzione potrebbe essere stata quella di dichiarare la classe Numero implementa Comparable, omettere compare Per l'implementazione e implementarla in alcune classi come Integer mentre lancia UnsupportedException in altre come AtomicInteger.

La mia ipotesi è che non implementando Comparable, offre maggiore flessibilità all'implementazione delle classi per implementarlo o meno. Tutti i numeri comuni (intero, lungo, doppio, ecc.) Implementano Comparable. Puoi ancora chiamare Collections.sort finché gli elementi stessi implementano Comparable.

Guardando la gerarchia di classi. Classi wrapper come Long, Integer, ecc., Implementano Comparable, ovvero un Integer è paragonabile a un intero e un long è paragonabile a un long, ma non puoi mescolarli. Almeno con questo paradigma dei generici. Il che immagino risponda alla tua domanda "perché".

byte (primitivo) è un int (primitivo). I primitivi hanno un solo valore alla volta.
Le regole di progettazione del linguaggio lo consentono.

int i = 255

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

Un Byte non è un Integer. Number è un compareTo(Number number1, Number number2) e un <=> è un <=>. <=> gli oggetti possono avere più di un valore contemporaneamente.

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

Se un <=> è un <=> e un <=> è un <=>, quale valore utilizzerai nel metodo <=>?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top