2010-08-23 10 views
9

¿Cómo puedo crear genéricamente un cero de un tipo numérico arbitrario?Crear un valor cero de una subclase de Número genérico

Aquí hay un ejemplo de juguete: una función que convierte un número nulo en cero.

static <T extends Number> T zeroIfNull(T value) { 
    return value == null ? 0 : value; 
} 

Esto no se compila porque el cero literal es de tipo int, y necesito convertir a que escriba T.

¿Es posible hacer esto en absoluto?

Respuesta

2

cero ni siquiera se menciona en la clase Number. Si debe hacerlo, y yo recomendaría evitar null s, es quizás:

public static <T> T coalesce(T a, T b) { 
    return a==null ? b : a; 
} 

También podría crear una interfaz genérica para los números de manipulación con las características que tienen sentido para su código:

interface NumberOps<T extends Number> { 
    T zeroIfNull(T value); 
} 
+0

Este es el enfoque más elegante. Ojalá Java tuviera un operador coalescente. Tal vez debería usar C# :) –

3

¿Es posible hacer esto en absoluto?
No realmente. Por un lado, cuando el valor es nulo, ¿cómo sabría el método qué implementación del número devolver?

+0

Sí, tienes razón, por supuesto. Tipo de borrado me muerde de nuevo. –

+0

@Bennett No se trata solo del borrado de tipos. Si declara _Número num = null; _ y llama a _zeroIfNull (num) _, incluso sin borrado de tipo será confuso. –

+0

Si cambia la firma ligeramente estática T zeroIfNull (valor T, clase clazz), puede superar el borrado de tipo. Aún debe decidir qué hacer cuando T no es una de las clases estándar. (E.G. Prime modela los números primos 2,3, ... y no tiene valor cero. ¿Qué hace zeroIfNull (null, Prime.class)?) – emory

1

intento:

return value == null ? (T)Integer.valueOf(0) : value; 
+0

El único problema es que siempre devuelve cero de tipo entero. (Además, puede reemplazar "Integer.valueOf (0)" por "0" debido al autoboxing) –

+0

Entero es una especie de Número. Está bien que return , y dado que se pasa un nulo (sin tipo específico), Integer está OK. –

+0

Intenté esto, e incluso podría funcionar en mi caso ya que el valor devuelto solo se convierte en una Cadena con 'toString()' de todos modos. Pero si lo llamé con un valor de BigInteger y luego intenté llamar a 'abs()', las cosas se pondrían feas. –

-1
No

que elegante, pero creo que en el caso más similar, eso es lo que podemos hacer:

static <T extends Number> T zeroIfNull(T value, Class<T> clazz) {...} 

y cuando se utiliza:

BigDecimal v = zeroIfNull(orignalVal, BigDecimal.class); 
+0

¿Pero cómo implementa el método? –

-1
import java . math . * ; 

class Numbers 
{ 
    public static < T extends Number > T zeroIfNull (T number , Class<T> clazz) throws IllegalArgumentException 
    { 
    if (clazz == Integer . class) 
     { 
     return zeroIfNull (number , clazz , 0) ; 
     } 
    else if (clazz == Double . class) 
     { 
     return zeroIfNull (number , clazz , 0) ; 
     } 
    else if (clazz == BigInteger . class) 
     { 
     return zeroIfNull (number , clazz , BigInteger . ZERO) ; 
     } 
    // add a whole bunch more if statements 
    throw new IllegalArgumentException ("Unexpected Number Class " + clazz . getName () + " with possibly undefined zero value.") ; 
    } 

    private static < T extends Number > T zeroIfNull (T number , Class<T> clazz , Object zero) 
    { 
    if (number == null) 
     { 
     return (clazz . cast (zero)) ; 
     } 
    else 
     { 
     return (number) ; 
     } 
    } 
} 
+0

Hmmm. Supongo que 'Object zero' debe ser' T zero' en ese segundo método, para mayor claridad al menos. –

+0

@Bennett Entonces no compilaría. Tendría que agregar casting en el primer método. Si hace que el segundo método sea privado y solo lo llame desde el primer método, entonces los valores cero serán del tipo T y el molde no será un problema. – emory

+0

Oh, sí, por supuesto, tendrás que lanzar los ceros. Como dije, esto ayudaría a la claridad. –

-1

La siguiente es una mejora en mi primera respuesta.

import java . math . * ; 
import java . util . * ; 

class Numbers 
{ 
    private static final Map<Class<? extends Number>,Object> zeroes = new HashMap<Class<? extends Number>,Object> () ; 

    static 
    { 
     zeroes . put (Integer . class , new Integer (0)) ; 
     zeroes . put (Double . class , new Double (0.0)) ; 
     zeroes . put (BigInteger . class , BigInteger . ZERO) ; 
     // fill it up with all supported classes 
    } 

    public static < T extends Number > T zeroIfNull (T number , Class<T> clazz) throws IllegalArgumentException 
    { 
    if (number == null) // return zero (if we know what zero is) 
     { 
     if (zeroes . containsKey (clazz)) 
      { 
      return (clazz . cast (zeroes . get (clazz))) ; 
      } 
     throw new IllegalArgumentException ("Unexpected Number Class " + clazz . getName () + " with undefined zero value.") ; 
     } 
    return number ; 
    } 
} 
+0

Sí, este tipo de cosas es por qué hice la pregunta. Esperaba que hubiera una mejor manera. –

1

Este es una vieja pregunta, pero después de luchar con este problema desde hace algún tiempo se me ocurrió una solución que pueda ser de interés para los demás:

@SuppressWarnings("unchecked") 
public static <T extends Number> T getZero() { 
    return (T)((Integer)0); 
} 

esto funciona bien siempre y cuando se trata de operaciones aritméticas se puede convertir el Número ejemplo, a algún tipo conocido, por ejemplo:

double getSquareRoot(T x) { 
    return Math.sqrt(x.doubleValue()); 
} 

En tales situaciones, no importa el tipo de la variable si se trata de una instancia de Número, siempre es posible obtener un doble.

Cuestiones relacionadas