2009-09-01 16 views
5

Mi pregunta es inquietantemente similar a "Writing a generic class to handle built-in types" que incluye estar inspirado por el hecho de trabajar en una clase para manejar operaciones en matrices. Aunque esa pregunta se hizo usando C# y señaló un artículo en Generic Operators.¿Cómo manejar los números de forma genérica?

No lo entiendo. Número de Java no tiene un método Add para que pueda tener un método como:

public Number myAdd(Number a, Number b){ 
    return a.add(b); 
} 

Así que, ¿cómo manejar un caso en el que desea ser capaz de manejar múltiples tipos de números en Java?

Respuesta

4

El problema fundamental es con el sistema de tipo Java que es muy primitivo.

Dado que no existe una noción de un conjunto sellado de tipos en Java (tampoco es posible que Java infiera los tipos como Haskell) no hay forma de hacer un Número + Número general -> Número sin engaño.

Para los primitivos (y aquellos objetos como Integer que se pueden mapear automágicamente) tipos de promoción y la operación + es parte del lenguaje. (Y esta es una parte real del problema: ¿qué debería devolver el Número a + Número b donde a y b son de tipos diferentes?)

Si realmente quiere este comportamiento, tendrá que encontrar (o crear) su propia clase personalizada que utilice reflexión o una serie (de controles y) moldes y tal. Incluso si usa genéricos (recuerde que los genéricos son borrados por el tipo), será necesario realizar el casting.

Imagino que estos problemas son parte de la razón por la que Number es tan sosa como es.

+0

"lo que debería Número a + Número b devuelve donde a y b son de tipos diferentes" es casi todo el problema. :) –

-1

Personalmente, uso BigDecimals para casi todo (pero eso es principalmente porque trabajo con valores de moneda). Manejan todos los valores numéricos de cualquier tamaño. Por eso, en mi opinión, son un valor genérico y podrían usarse como tales en su ejemplo hipotético en lugar de la clase abstracta de Número. Todo se puede convertir en un BigDecimal, ¿por qué no usarlo?

public BigDecimal myAdd(BigDecimal a, BigDecimal b) { 
    return a.add(b); 
} 

EDIT: Para abordar BigBrothers comentario más abajo, siempre se puede utilizar el método doubleValue() para crear su propio método genérico. El único problema con esto es que usted puede perder precisión en algunos casos raros en los que alguien está pasando en un BigDecimal y es más grande que un Double.maxValue

public Number myAdd(Number a, Number b) { 
    return new BigDecimal(a.doubleValue() + b.doubleValue()); 
} 

Un BigDecimal es un número, por lo que regresan uno es de ningún consecuencia.

+0

Mientras veo lo que dices, esperaba algo más elegante, supongo. –

3

No lo entiendo. Número Java no hace tienen un método Add ...

Supongamos que java.lang.Number tenían un método o métodos add, ¿cómo definiría usted su firma? ¿Cómo definirías su semántica? ¿Cómo lidiarías con la aritmética de "modo mixto"?

Si bien es posible responder a estas preguntas y diseñar una API, es probable que el resultado sea difícil de usar correctamente. Además, es muy raro que una aplicación necesite realizar una aritmética "independiente de la representación". Por lo general, desea/necesita un control explícito sobre la forma en que se realiza la aritmética y las conversiones. (Las reglas de la promoción de tipo primitivo Java son ya bastante difícil para las personas a conseguir sus cabezas alrededor !!)

Con todo, creo que Sun ha nos hecho un buen servicio por no tratando de apoyar en la aritmética la API de número.

3

¿Qué tan bueno quieres que sea el resultado? Si la respuesta es "suficientemente bueno, sobre todo", entonces esto debe ser sufficent:

public Number myAdd(Number a, Number b){ 
    return a.doubleValue() + b.doubleValue(); 
} 

Pero si quieres algo que, por ejemplo, coincide con la semántica de promoción de primitivos de Java, lo que probablemente va a tener que escribir usted mismo Y luego tendrá que averiguar cuáles son las reglas para todas las combinaciones de implementaciones "no estándar" Number, incluidos BigDecimal, BigInteger, AtomicDouble, AtomicLong, todo en org.apache.commons.lang.mutable, y cualquier implementación aleatoria que alguien decida escribir el próximo martes .

No está claro qué es lo correcto en la mayoría de estos casos: convertir todo a BigDecimal, por ejemplo, no es una opción si uno de los argumentos es Apache Commons 'Fraction.ONE_THIRD; y además, hacer la conversión de manera general presenta los mismos problemas que hacer la adición de una manera general. Pero tener un método add() en Number requeriría cada implementación de Number para manejar todos estos casos, y esa es probablemente la razón por la que no está allí.

0

No se pueden agregar dos números, por las razones que otros señalan, pero puede agregar números del mismo tipo, y el resultado también será del mismo tipo. Puede crear aritmética genéricos en Java, con algo como esto:

interface Arithmetics<T> { 
    T add(T val1, T val2); 
} 

class IntegerArithmetics implements Arithmetics<Integer> { 
    Integer add(Integer val1, Integer val2) { return val1 + val2; } 
} 

//similarly DoubleArithmetics, BigIntegerArithmetics, ... 

Generic Java Math biblioteca hace exactamente eso para usted.

1

Una forma de implementar un método de adición genérico es permitir que el argumento de la mano izquierda infiera el tipo de devolución.

package mixins; 

import java.math.BigDecimal; 

public class Numbers { 

    public static boolean isZ(Number n) { 
     return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte; 
    } 

    public static boolean isR(Number n) { 
     return n instanceof Double || n instanceof Float; 
    } 

    public static BigDecimal add(BigDecimal a, Number b) { 
     if (b instanceof BigDecimal) { 
      return a.add((BigDecimal) b); 
     } else if (isZ(b)) { 
      return a.add(new BigDecimal(b.longValue())); 
     } else if (isR(b)) { 
      return a.add(new BigDecimal(b.doubleValue())); 
     } 
     throw new IllegalArgumentException("No valid big decimal translation for " + b.getClass()); 
    } 

    public static Integer add(Integer a, Number b) { 
     return a + b.intValue(); 
    } 

    public static Long add(Long a, Number b) { 
     return a + b.longValue(); 
    } 

    public static Float add(Float a, Number b) { 
     return a + b.floatValue(); 
    } 

    public static Double add(Double a, Number b) { 
     return a + b.doubleValue(); 
    } 
} 

Si esto se implementa como métodos estáticos, puede usar importaciones estáticas.

import static mixins.Numbers.*; 

public class Example { 

    public static void main(String[] args) { 
     BigDecimal fortytwo = new BigDecimal(42); 
     BigDecimal fiftyfive = add(fortytwo, 13); 
     System.out.println(fiftyfive); 
    } 

} 
Cuestiones relacionadas