2010-10-13 4 views
10

Je souhaite créer une méthode qui compare un nombre mais peut avoir une entrée qui est l'une des sous-classes de Number.Génériques Java et la classe Number

Je l'ai regardé faire cela de la manière suivante ...

public static <T extends Number> void evaluate(T inputNumber) { 
    if (inputNumber >= x) { 
    ... 
    } 
} 

Je dois obtenir le primative réelle avant que je puisse effectuer la comparaison, la classe Number a des méthodes pour récupérer ce pour chaque primative mais Je veux un moyen propre de choisir le bon.

Est-ce possible?

Vive

+0

connexes: [Java: méthodes génériques et les numéros] (http://stackoverflow.com/questions/3850970/java-generic-methods-and-numbers). – BalusC

Répondre

4

Malheureusement, il n'y a aucun moyen d'obtenir le type primitif du type wrapper sans avoir recours aux blocs if/else.

Le problème est qu'il ne serait tout simplement pas possible de mettre en œuvre une telle méthode de manière générique. Voici quelques approches possibles en apparence que l'on pourrait attendre à trouver dans la classe Number:

public abstract X getPrimitiveValue(); 

Ce serait bien, ne serait-il? Mais il est impossible. Il n'y a pas possible X qui pourrait être une abstraction sur int, float, double etc.

public abstract Class<?> getCorrespondingPrimitiveClass(); 

Cela ne va pas aider non plus, parce qu'il n'y a aucun moyen de instancier des classes primitives. La seule chose que vous pouvez faire est commun à tous les types est d'utiliser les méthodes longValue() ou doubleValue(), mais de toute façon vous perdez des informations si vous avez affaire à un mauvais type.

Donc non: la hiérarchie des nombres Java n'est tout simplement pas adaptée pour résoudre de tels problèmes de manière générique.

0

dans ce cas, vous pouvez utiliser une méthode ordinaire sans génériques

+0

Donc, si la méthode est d'accepter un double ou un entier, il faudrait alors deux méthodes avec des signatures différentes ou ai-je oublié quelque chose? –

+0

@Mr_R: Si vous utilisez Java 5, l'autoboxing le convertira en Double et Entier, tous deux implémentant Number. –

+2

Profitez du polymorphisme et offrez simplement une méthode 'evaluate (Number)' –

8

L'API Number n'offre pas un moyen propre pour obtenir la valeur; vous devez utiliser instanceof. Une solution consiste à «plier» les valeurs en deux types: long et double. De cette façon, vous pouvez utiliser ce code:

if(inputNumber instanceof Float || inputNumber instanceof Double) { 
    double val = inputNumber.doubleValue(); 
    ... 
} else { 
    long val = inputNumber.longValue(); 
    ... 
} 

Notez que cela ne fonctionne que pour les types de numéro standard, mais Number est également mis en œuvre par un grand nombre d'autres types (AtomicInteger, BigDecimal).

Si vous voulez soutenir tous les types, une astuce consiste à utiliser BigDecimal:

BigDecimal value = new BigDecimal(inputNumber.toString()); 

Cela devrait toujours travailler et vous donner le résultat le plus exact.

2

Les méthodes avec <T extends Number> sont toujours un problème, car vous ne pouvez pas vraiment faire quoi que ce soit sur un nombre (tous les opérateurs sont définis pour les enfants). Vous devez soit faire une tonne de instanceof pour chaque enfant de Number et traiter ce cas en le castant au sous-type. Ou (mieux je pense - c'est la façon dont Sun le fait) est juste d'avoir une méthode pour chaque type enfant, en profitant éventuellement de la boxe/unboxing pour les opérateurs comme +, -,> etc où cela est possible (tous les wrappers, pour BigInteger/BigDecimal ou tous les types personnalisés).

1

Si c'est vraiment seulement de comparer l'argument à une autre valeur du même paramètre de type, vous pouvez effectuer les opérations suivantes (juste ajouter à T x pour simplifier) ​​

public static <T extends Number & Comparable<? super Number>> int evaluate(T inputNumber, T x) { 
      if (inputNumber.compareTo(x) > 0) { ... } 
    }