Question

    

Cette question a déjà une réponse ici:

         

Quelqu'un sait-il pourquoi java.lang.Number ne met pas en œuvre Comparable? Cela signifie que vous ne pouvez pas trier les Number s avec Collections.sort ce qui me semble un peu étrange.

Mise à jour après la discussion:

Merci pour toutes les réponses utiles. J'ai fini par faire d'autres recherches sur ce sujet .

L'explication la plus simple de la non-implémentation de java.lang.Number dans Comparable est liée à des problèmes de mutabilité.

Pour un peu d’examen, AtomicInteger est le super-type abstrait composé de AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short. , compareTo et <=>. Sur cette liste, <=> et <=> ne pas implémenter <=>.

En fouillant, j'ai découvert qu'il n'est pas recommandé d'appliquer <=> sur des types mutables, car les objets peuvent changer pendant ou après la comparaison, rendant le résultat de la comparaison inutile. Les deux <=> et <=> sont mutables. Les concepteurs d’API avaient prévu de ne pas <=> implémenter <=> car cela aurait contraint l’implémentation de futurs sous-types. En effet, <=> et <=> ont été ajoutés à Java 1.5 longtemps après la mise en œuvre initiale de <=>.

Outre la mutabilité, il existe probablement d'autres considérations ici aussi. Une <=> implémentation dans <=> devrait promouvoir toutes les valeurs numériques en <=> car elle est capable de prendre en charge tous les <=> sous-types. L’implication de cette promotion en termes de mathématiques et de performances m’est un peu obscure, mais j’ai l’intuition de trouver la solution parfaite.

Était-ce utile?

La solution

Il convient de mentionner que l'expression suivante:

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

est toujours false, ce qui a tendance à faire trébucher tout le monde à un moment ou à un autre. Ainsi, non seulement vous ne pouvez pas comparer des Number s arbitraires, mais vous ne pouvez même pas déterminer s'ils sont égaux ou non.

De plus, avec les types primitifs réels (float, double), déterminer si deux valeurs sont égales est délicat et doit être effectué avec une marge d'erreur acceptable. Essayez un code comme:

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

et vous vous retrouverez avec une petite différence.

Revenons donc à la question de la création de Comparable doubleValue(). Comment le mettriez-vous en œuvre? Utiliser quelque chose comme Byte ne le ferait pas de manière fiable. Rappelez-vous que les Short sous-types sont:

  • Integer;
  • Long;
  • AtomicInteger;
  • AtomicLong;
  • Float;
  • Double;
  • BigInteger;
  • BigDecimal;
  • compareTo(); et
  • byteValue().

Pourriez-vous coder une méthode shortValue() fiable qui ne se résume pas à une série d'instructions if instanceof? intValue() les instances ne disposent que de six méthodes:

  • longValue();
  • floatValue();
  • <=>;
  • <=>;
  • <=>; et
  • <=>.

Je suppose donc que Sun a pris la décision (raisonnable) selon laquelle <=> s ne correspondait <<> à une instance d’eux-mêmes.

Autres conseils

Pour la réponse, consultez la page bogue Java bogue 4414323 . Vous pouvez également trouver une discussion sur comp. lang.java.programmer

Pour citer la réponse de Sun au rapport de bogue de 2001:

  

Tous les " nombres " ne sont pas comparables;   comparable suppose une commande totale de   les nombres sont possibles. Ce n'est même pas   vrai des nombres à virgule flottante; NaN   (pas un nombre) n'est ni inférieur à,   supérieur à, ni égal à aucun   valeur en virgule flottante, même elle-même.   {Float, Double} .compare imposer un total   commande différente de la commande   de la virgule flottante & "; < &"; et " = "   les opérateurs. En outre, comme actuellement   mis en œuvre, les sous-classes de Number   ne sont comparables à d'autres cas   de la même classe. Il y a d'autres   cas, comme les nombres complexes, où aucun   la commande totale standard existe,   bien que l'on puisse être défini. Dans   bref, qu’il s’agisse ou non d’une sous-classe de   Le nombre est comparable devrait être laissé comme   une décision pour cette sous-classe.

pour mettre en œuvre un nombre comparable, vous devez écrire du code pour chaque paire de sous-classes. Il est plus facile de permettre aux sous-classes de mettre en œuvre des méthodes comparables.

Très probablement, car il serait plutôt inefficace de comparer des nombres - la seule représentation dans laquelle chaque nombre puisse s’adapter pour permettre une telle comparaison serait BigDecimal.

Les sous-classes non atomiques de Number implémentent elles-mêmes Comparable.

Les atomes sont mutables, donc ne peuvent pas implémenter de comparaison atomique.

Vous pouvez utiliser Transmorph pour comparer des nombres à l'aide de sa 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);

Pour tenter de résoudre le problème initial (trier une liste de nombres), une option consiste à déclarer la liste d'un type générique qui étend un nombre et implémente Comparable.

Quelque chose comme:

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

il n’existe pas de comparaison standard pour les nombres de types différents. Cependant, vous pouvez écrire votre propre comparateur et l'utiliser pour créer un TreeMap & Lt; Number, Object & Gt ;, TreeSet & Lt; Number & Gt; ou Collections.sort (Liste < Numéro > ;, Comparateur) ou Arrays.sort (Numéro [], Comparateur);

Écrivez votre propre comparateur

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

pourquoi cela aurait été une mauvaise idée? :

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
}

une autre option consistait peut-être à déclarer la classe Numérique implémente Comparable, à omettre l'implémentation de compareTo et à l'implémenter dans certaines classes telles qu'Integer, tandis que lève UnsupportedException dans d'autres, comme AtomicInteger.

Je suppose qu'en ne mettant pas en oeuvre Comparable, cela donne plus de flexibilité à la mise en oeuvre de classes pour l'implémenter ou non. Tous les nombres communs (Integer, Long, Double, etc.) implémentent Comparable. Vous pouvez toujours appeler Collections.sort tant que les éléments eux-mêmes implémentent Comparable.

En regardant la hiérarchie des classes. Les classes wrapper telles que Long, Integer, etc., implémentent Comparable, c’est-à-dire qu’un Integer est comparable à un entier et qu'un long est comparable à un long, mais vous ne pouvez pas les mélanger. Au moins avec ce paradigme générique. Ce qui, je suppose, répond à votre question "pourquoi".

byte (primitive) est une int (primitive). Les primitives n'ont qu'une valeur à la fois.
Les règles de conception du langage le permettent.

int i = 255

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

Un Byte n’est pas un Integer. Number est un compareTo(Number number1, Number number2) et un <=> est un <=>. <=> Les objets peuvent avoir plusieurs valeurs à la fois.

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

Si un <=> est un <=> et un <=> est un <=>, quelle valeur utiliserez-vous dans la méthode <=>?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top