Pregunta

Esta pregunta ya tiene una respuesta aquí:

¿Alguien sabe por qué java.lang.Number no aplicar Comparable?Esto significa que usted puede ordenar Numbers con Collections.sort que a mí me parece un poco extraño.

Post discusión de la actualización:

Gracias por todos los útiles las respuestas.Yo terminé haciendo poco más de investigación acerca de este tema.

La explicación más simple para por qué java.lang.Número no implementa Comparable está arraigada en la mutabilidad de las preocupaciones.

Para un poco de revisión, java.lang.Number es el resumen de super-tipo de AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long y Short.En esa lista, AtomicInteger y AtomicLong para no implementar Comparable.

Cavar alrededor, descubrí que no es una buena práctica para implementar Comparable en mutable tipos debido a que los objetos pueden cambiar durante o después de la comparación de representación en el resultado de la comparación inútil.Ambos AtomicLong y AtomicInteger son mutables.La API de los diseñadores tuvieron la previsión de no tener Number implementar Comparable porque habría limitado la implementación de futuro de los subtipos.De hecho, AtomicLong y AtomicInteger se han añadido en Java 1.5 mucho tiempo después de java.lang.Number fue inicialmente implementado.

Aparte de mutabilidad, probablemente hay otras consideraciones aquí también.Un compareTo aplicación en Number tendría que promover todos los valores numéricos para BigDecimal porque es capaz de albergar a todos los Number sub-tipos.La implicación de que la promoción en términos de las matemáticas y el rendimiento es un poco claro para mí, pero mi intuición considera que la solución desacoplados.

¿Fue útil?

Solución

Vale la pena mencionar que la siguiente expresión:

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

siempre es false, lo que tiende a hacer tropezar a todos en algún momento u otro. Por lo tanto, no solo no puede comparar Number s arbitrarios, sino que ni siquiera puede determinar si son iguales o no.

Además, con los tipos primitivos reales (float, double), determinar si dos valores son iguales es complicado y debe hacerse dentro de un margen de error aceptable. Pruebe código como:

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

y te quedarás con una pequeña diferencia.

Volvamos al tema de hacer Comparable doubleValue(). ¿Cómo lo implementaría? Usar algo como Byte no lo haría de manera confiable. Recuerde que los Short subtipos son:

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

¿Podría codificar un método confiable shortValue() que no se convierta en una serie de declaraciones if instanceof? intValue() las instancias solo tienen seis métodos disponibles:

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

Así que supongo que Sun tomó la decisión (razonable) de que <=> s eran solo <=> a instancias de sí mismos.

Otros consejos

Para la respuesta, consulte Java bugparade bug 4414323 . También puede encontrar una discusión en comp. lang.java.programmer

Para citar la respuesta de Sun al informe de error de 2001:

  

Todos " números " no son comparables;   comparable supone una ordenación total de   Los números son posibles. Esto ni siquiera es   verdadero de los números de coma flotante; Yaya   (no es un número) no es menor que,   mayor que, ni igual a cualquier   valor de coma flotante, incluso a sí mismo.   {Float, Double} .compare impone un total   ordenar diferente del pedido   del punto flotante " < " y " = "   operadores. Además, como actualmente   implementado, las subclases de Number   solo son comparables a otras instancias   de la misma clase Hay otros   casos, como números complejos, donde no   existe un pedido total estándar,   aunque uno podría definirse. En   corto, sea o no una subclase de   El número es comparable debe dejarse como   una decisión para esa subclase.

para implementar un número comparable, tendría que escribir código para cada par de subclase. En cambio, es más fácil permitir que las subclases implementen productos comparables.

Muy probablemente porque sería bastante ineficiente comparar números: la única representación en la que cada Número puede encajar para permitir tal comparación sería BigDecimal.

En cambio, las subclases no atómicas de Number implementan Comparable en sí.

Los atómicos son mutables, por lo que no se puede implementar una comparación atómica.

Puede usar Transmorph para comparar números usando su clase 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);

Para tratar de resolver el problema original (ordenar una lista de números), una opción es declarar la lista de un tipo genérico que extiende Número e implementa Comparable.

Algo así como:

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

no hay comparación stardard para Números de diferentes tipos. Sin embargo, puede escribir su propio comparador y usarlo para crear un TreeMap & Lt; Number, Object & Gt ;, TreeSet & Lt; Number & Gt; o Collections.sort (List < Number > ;, Comparator) o Arrays.sort (Number [], Comparator);

Escribe tu propio comparador

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

¿por qué habría sido una mala 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
}

otra opción puede haber sido declarar implementaciones de número de clase Comparable, omitir la implementación compareTo e implementarla en algunas clases como Integer mientras lanza UnsupportedException en otras como AtomicInteger.

Supongo que al no implementar Comparable, da más flexibilidad para implementar clases para implementarlo o no. Todos los números comunes (Entero, Largo, Doble, etc.) implementan Comparable. Todavía puede llamar a Collections.sort siempre que los elementos mismos implementen Comparable.

Mirando la jerarquía de clases. Clases de envoltura como Long, Integer, etc., implementan Comparable, es decir, un Integer es comparable a un entero, y un long es comparable a un long, pero no puede mezclarlos. Al menos con este paradigma genérico. Lo que supongo responde a tu pregunta 'por qué'.

byte (primitivo) es un int (primitivo).Primitivas tienen sólo un valor en un momento.
El lenguaje de reglas de diseño lo permite.

int i = 255

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

Un Byte no es un Integer. Byte es un Number y un Integer es un Number. Number los objetos pueden tener más de un valor al mismo tiempo.

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

Si un Byte es un Integer y un Integer es un Number, Que un valor se utiliza en la compareTo(Number number1, Number number2) método?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top