2010-08-23 7 views
9

Comment créer un zéro d'un type numérique arbitraire?Créer une valeur nulle d'une sous-classe Number générique

Voici un exemple de jouet: une fonction qui convertit un nombre nul en zéro.

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

Cela ne compile pas parce que le zéro littéral est de type int, et je dois convertir que pour taper T.

Est-il possible de faire cela du tout?

Répondre

2

Zéro n'est même pas mentionné dans la classe Number. Si vous devez le faire, et je conseille d'éviter null s, est peut-être:

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

Vous pouvez également créer une interface générique pour le traitement des nombres avec des fonctionnalités qui ont du sens à votre code:

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

C'est l'approche la plus élégante. Je souhaite que Java ait un opérateur coalesce. Peut-être que je devrais juste utiliser C# :) –

3

Est-il possible de faire cela du tout?
Pas vraiment. D'une part, quand la valeur est nulle, comment la méthode pourrait-elle savoir quelle implémentation de Number retourner?

+0

Oui, vous avez raison, bien sûr. L'effacement de type me mord encore. –

+0

@Bennett Ce n'est pas seulement une question d'effacement de type. Si vous déclarez _Number num = null; _ et appelez _zeroIfNull (num) _, même sans effacement de type, cela créera de la confusion. –

+0

Si vous modifiez la signature légèrement statique T zeroIfNull (valeur T, classe clazz) alors vous pourriez surmonter l'effacement de type. Vous devez encore décider quoi faire quand T n'est pas l'une des classes standard. (E.G. Prime modélise les nombres premiers 2,3, ... et n'a pas de valeur zéro Que fait zeroIfNull (null, Prime.class)?) – emory

1

essayer:

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

Le seul problème est qu'il retourne toujours zéro de type entier. (En outre, vous pouvez remplacer "Integer.valueOf (0)" par "0" en raison de l'autooboxing) –

+0

Entier est une sorte de nombre. C'est bon pour return , et comme un null (aucun type spécifique) n'est passé, Integer est OK. –

+0

J'ai essayé ceci, et cela pourrait même fonctionner dans mon cas puisque la valeur retournée n'est convertie en String qu'avec 'toString()' de toute façon. Mais si je l'ai appelé avec une valeur BigInteger, puis essayé d'appeler 'abs()' alors les choses deviendraient moche. –

-1

Non pas élégant, mais je pense que dans le cas le plus similaire, c'est ce que nous pouvons faire:

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

et lorsqu'il est utilisé:

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

Mais comment implémentez-vous la méthode? –

-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. Je suppose que 'Object zero' devrait être' T zéro' dans cette seconde méthode, pour plus de clarté. –

+0

@Bennett Alors il ne compilerait pas. Vous auriez à ajouter le casting dans la première méthode. Si vous rendez la seconde méthode privée et que vous l'appelez seulement à partir de la première méthode, alors les valeurs nulles seront de type T et la distribution ne sera pas un problème. – emory

+0

Oh oui, bien sûr, vous devrez jeter les zéros. Comme je l'ai dit, cela aiderait à la clarté. –

-1

Ce qui suit est une amélioration sur ma première réponse.

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

Oui, ce genre de chose est la raison pour laquelle j'ai posé la question. J'espérais qu'il y aurait un meilleur moyen. –

1

Cette est une vieille question, mais après avoir lutté avec cette question depuis un certain temps je suis venu avec une solution qui peut intéresser les autres:

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

cela fonctionne bien aussi longtemps que lorsqu'il s'agit de l'arithmétique des opérations que vous pouvez convertir le Nombre instance à un certain type connu, par exemple:

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

Dans de telles situations, il n'a pas d'importance du type de la variable si elle est une instance de Nombre il est toujours possible d'obtenir un double.

Questions connexes