method generic example create java generics numbers comparable

create - java generics method example



Comparando los valores de dos nĂºmeros genĂ©ricos (11)

¿Qué hay de este? Definitivamente no es agradable, pero trata con todos los casos necesarios mencionados.

public class SimpleNumberComparator implements Comparator<Number> { @Override public int compare(Number o1, Number o2) { if(o1 instanceof Short && o2 instanceof Short) { return ((Short) o1).compareTo((Short) o2); } else if(o1 instanceof Long && o2 instanceof Long) { return ((Long) o1).compareTo((Long) o2); } else if(o1 instanceof Integer && o2 instanceof Integer) { return ((Integer) o1).compareTo((Integer) o2); } else if(o1 instanceof Float && o2 instanceof Float) { return ((Float) o1).compareTo((Float) o2); } else if(o1 instanceof Double && o2 instanceof Double) { return ((Double) o1).compareTo((Double) o2); } else if(o1 instanceof Byte && o2 instanceof Byte) { return ((Byte) o1).compareTo((Byte) o2); } else if(o1 instanceof BigInteger && o2 instanceof BigInteger) { return ((BigInteger) o1).compareTo((BigInteger) o2); } else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal) { return ((BigDecimal) o1).compareTo((BigDecimal) o2); } else { throw new RuntimeException("Ooopps!"); } } }

Quiero comparar con las variables, tanto de tipo T extends Number . Ahora quiero saber cuál de las dos variables es mayor que la otra o igual. Lamentablemente, todavía no sé el tipo exacto, solo sé que será un subtipo de java.lang.Number . ¿Cómo puedo hacer eso?

EDITAR : TreeSet otra solución usando TreeSet s, que en realidad funcionaba con ordenamiento natural (por supuesto, funciona, todas las subclases de Number implementan Comparable excepto AtomicInteger y AtomicLong). Por lo tanto, perderé valores duplicados. Al usar List s, Collection.sort() no aceptará mi lista debido a mismatchs limitados. Muy insatisfactorio


Después de haber hecho una pregunta similar y estudiar las respuestas aquí, se me ocurrió lo siguiente. Creo que es más eficiente y más robusto que la solución dada por Gustafc:

public int compare(Number x, Number y) { if(isSpecial(x) || isSpecial(y)) return Double.compare(x.doubleValue(), y.doubleValue()); else return toBigDecimal(x).compareTo(toBigDecimal(y)); } private static boolean isSpecial(Number x) { boolean specialDouble = x instanceof Double && (Double.isNaN((Double) x) || Double.isInfinite((Double) x)); boolean specialFloat = x instanceof Float && (Float.isNaN((Float) x) || Float.isInfinite((Float) x)); return specialDouble || specialFloat; } private static BigDecimal toBigDecimal(Number number) { if(number instanceof BigDecimal) return (BigDecimal) number; if(number instanceof BigInteger) return new BigDecimal((BigInteger) number); if(number instanceof Byte || number instanceof Short || number instanceof Integer || number instanceof Long) return new BigDecimal(number.longValue()); if(number instanceof Float || number instanceof Double) return new BigDecimal(number.doubleValue()); try { return new BigDecimal(number.toString()); } catch(final NumberFormatException e) { throw new RuntimeException("The given number (/"" + number + "/" of class " + number.getClass().getName() + ") does not have a parsable string representation", e); } }


El número primitivo de Java más "genérico" es el doble, por lo que simplemente

a.doubleValue() > b.doubleValue()

debería ser suficiente en la mayoría de los casos, pero ... aquí hay problemas sutiles al convertir los números al doble. Por ejemplo, lo siguiente es posible con BigInteger:

BigInteger a = new BigInteger("9999999999999992"); BigInteger b = new BigInteger("9999999999999991"); System.out.println(a.doubleValue() > b.doubleValue()); System.out.println(a.doubleValue() == b.doubleValue());

resultados en:

false true

Aunque espero que este sea un caso muy extremo, esto es posible. Y no, no hay una forma genérica 100% precisa. La interfaz numérica no tiene un método como la conversión de exactValue () a algún tipo capaz de representar el número de manera perfecta sin perder ninguna información.

De hecho, tener tales números perfectos es imposible en general, por ejemplo, representar el número Pi es imposible usando cualquier aritmética usando espacio finito.


Esto debería funcionar para todas las clases que extienden Número, y son Comparables consigo mismas. Al agregar el & Comparable, permite eliminar todas las comprobaciones de tipo y proporciona controles de tipo de tiempo de ejecución y lanzamiento de errores de forma gratuita en comparación con la respuesta de Sarmun.

class NumberComparator<T extends Number & Comparable> implements Comparator<T> { public int compare( T a, T b ) throws ClassCastException { return a.compareTo( b ); } }


Esto debería funcionar para todas las clases que extienden Número, y son Comparables consigo mismas.

class NumberComparator<T extends Number> implements Comparator<T> { public int compare(T a, T b){ if (a instanceof Comparable) if (a.getClass().equals(b.getClass())) return ((Comparable<T>)a).compareTo(b); throw new UnsupportedOperationException(); } }


Si sus instancias numéricas nunca son atómicas (es decir, AtomicInteger), entonces puede hacer algo como:

private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { Class<? extends Number> n1Class = n1.getClass(); if (n1Class.isInstance(n2)) { Method compareTo = n1Class.getMethod("compareTo", n1Class); return (Integer) compareTo.invoke(n1, n2); } return -23; }

Esto es porque todos los Number no atómicos implementan Comparable

EDITAR :

Esto es costoso debido a la reflexión: sé

EDICION 2 :

Por supuesto, esto no se aplica a un caso en el que desea comparar decimales con ints o algo así ...

EDIT 3 :

Esto supone que no hay descendientes de Número definidos a medida que no implementen Comparable (gracias @DJClayworth)


Simplemente puede usar Number''s doubleValue() método Number''s doubleValue() para compararlos; Sin embargo, es posible que los resultados no sean lo suficientemente precisos para sus necesidades.


Supongamos que tienes algún método como:

public <T extends Number> T max (T a, T b) { ... //return maximum of a and b }

Si sabe que solo hay números enteros, largos y dobles se pueden pasar como parámetros, entonces puede cambiar la firma del método a:

public <T extends Number> T max(double a, double b) { return (T)Math.max (a, b); }

Esto funcionará para byte, corto, entero, largo y doble.

Si supone que se pueden pasar BigInteger o BigDecimal o una combinación de flotantes y dobles, entonces no puede crear un método común para comparar todos estos tipos de parámetros.


Una solución que funciona (pero frágil) es algo como esto:

class NumberComparator implements Comparator<Number> { public int compare(Number a, Number b){ return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString())); } }

Sin embargo, aún no es genial, ya que cuenta con que toString devuelva un valor analizable por BigDecimal (que las clases de Java Number estándar, pero que el contrato de Number no exige).

Editar, siete años después: Como se señala en los comentarios, hay (¿al menos?) Tres casos especiales que puede producir toString para tener en cuenta:


Una solución que podría funcionar para usted es trabajar no con T extends Number pero con T extends Number & Comparable . Este tipo significa: " T solo se puede establecer en tipos que implementen ambas interfaces".

Eso le permite escribir código que funciona con todos los números comparables. Estáticamente tipado y elegante.

Esta es la misma solución que propone BennyBoy, pero funciona con todo tipo de métodos, no solo con clases de comparación.

public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) { if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger"); } public void test() { compfunc(2, 1); // Works with Integer. compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable. compfunc(2, 1.0); // Compilation error! Different types. compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable }


if(yourNumber instanceof Double) { boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue(); // [...] }

Nota: La instanceof verificación no es necesariamente necesaria, depende de cómo exactamente quiera compararlas. Por supuesto, siempre .doubleValue() usar .doubleValue() , ya que cada número debe proporcionar los métodos enumerados here .

Editar : como se indica en los comentarios, tendrá que (siempre) verificar BigDecimal y sus amigos. Pero proporcionan un método .compareTo() :

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0; }