¿Por qué no en java.lang.Número de implementar Comparables?[duplicar]
-
20-08-2019 - |
Pregunta
Esta pregunta ya tiene una respuesta aquí:
- La comparación de los valores de dos Números genéricos 12 respuestas
¿Alguien sabe por qué java.lang.Number
no aplicar Comparable
?Esto significa que usted puede ordenar Number
s 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.
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?