2013-04-16 18 views
7

mi piacerebbe creare un metodo generico che fa in modo efficace questo:generici Java e tipi numerici

class MyClass { 

    static <T extends Number> T sloppyParseNumber(String str) { 
     try { 
      return T.valueOf(str); 
     } catch (Exception e) { 
      return (T)0; 
     } 
    } 
} 

Ora sopra non compilare per due motivi: non c'è nessun metodo Number.valueOf() e 0 non possono essere lanciati a T.

Esempio di utilizzo:

String value = "0.00000001"; 

System.out.println("Double: " + MyClass.<Double>sloppyParseNumber(value)); 
System.out.println("Float: " + MyClass.<Float>sloppyParseNumber(value)); 

double d = MyClass.sloppyParseNumber(value); 
float f = MyClass.sloppyParseNumber(value); 

sta attuando sopra metodo generico possibile con Java? Se sì, come? Se no, qual è un buon approccio alternativo?


Modifica: sembra che ci siano alcuni possibili duplicati, ma non ne ho trovato uno, che copre esattamente questo. Spero che ci sia qualche trucco da prendere, che consentirebbe queste due operazioni: analizzare la stringa in una sottoclasse Number e restituire il valore 0 per una sottoclasse Number.

+1

Il ['numero'] (http://docs.oracle.com/javase/6/docs/api/java/lang/Number.html) La classe astratta non contiene alcun metodo per analizzare la stringa in un tipo, poiché l'analisi viene eseguita da un tipo specifico. Un suggerimento è di non usare generici, ma di creare funzioni come '... ToInteger',' ... ToDouble' e così via. –

+0

Non penso che ci sia un modo semplice per farlo. Vorrei semplicemente usare qualcosa come 'new MyClass (value) .asDouble();' e implementare manualmente i metodi 'asInteger',' asLong', 'asFloat',' asDouble' ecc. – assylias

+3

'T.valueOf()' wouldn ' t funziona anche se esisteva un metodo statico 'Number.valueOf()'; semplicemente non puoi usare tipi generici come questo. –

risposta

3

Sono d'accordo al 100% con TofuBeer. Ma nel caso in cui si desidera evitare la verbosità per amor di tempo, questo dovrebbe anche fare:

static <T extends Number> T sloppyParseNumber(String str,Class<T> clas) { 

    if (clas == null) throw new NullPointerException("clas is null"); 

    try { 

     if(clas.equals(Integer.class)) { 
      return (T) Integer.valueOf(str); 
     } 
     else if(clas.equals(Double.class)) { 
      return (T) Double.valueOf(str); 
     } 
     //so on 

    catch(NumberFormatException|NullPointerException ex) { 
     // force call with valid arguments 
     return sloppyParseNumber("0", clas); 
    } 

    throw new IllegalArgumentException("Invalid clas " + clas); 

} 

ma puramente da T, non è possibile ottenere il tipo in fase di esecuzione.

+0

Questo sembra l'approccio più vicino a quello che ho chiesto nella domanda, anche se preferirei che la risposta includesse le eccezioni e la restituzione della sottoclasse giusta (quindi restituire 'T' e parametro' Classe clas', suppongo). – hyde

+0

Modificato. Grazie. – Jatin

3

I generici Java forniscono solo verifiche della durata della compilazione e le informazioni sul tipo vengono praticamente eliminate dopo la compilazione. Quindi la dichiarazione T.valueOf non è possibile in Java. La soluzione è quella di andare in modo dettagliato come già menzionato nei commenti. Inoltre, c'è qualche ragione per cui vuoi fare MyClass.<Double>sloppyParseNumber(value) ma non MyClass.sloppyParseDouble(value) dato che stai comunque specificando il tipo al momento della compilazione?

+0

@hyde: non sono sicuro di aver capito. Quando dici "è la stessa funzione di sempre e specificare il tipo non fa nulla", perché pensi di specificare il tipo non fa nulla? Nell'esempio aggiornato, quale sarà il tipo di ritorno di 'sloppyParseNumber'? Come farà a sapere se il numero passato deve essere analizzato come 'BigDecimal',' Double', 'Float',' Integer' ecc.? –

+0

Proviamo di nuovo: Scopo di 'MyClass. sloppyParseNumber' avrebbe dovuto rendere il metodo restituito oggetto Double, ma in effetti i generici Java non funzionano in questo modo, il metodo non può conoscere il tipo specificato dal chiamante in '<>'. – hyde

2

I metodi statici sono associati al tipo, poiché il tipo è, nel migliore dei casi, Number e Number non ha un metodo valueOf quello che si sta cercando non funzionerà.

Il modo più semplice è quello di fare solo un certo numero di metodi statici come sloppyParseInt, sloppyParseFloat, ecc ...

Si potrebbe fare qualcosa di simile, non sono sicuro che mi piace, e può probabilmente essere migliorato:

public class Main 
{ 
    private static final Map<Class<? extends Number>, NumberConverter> CONVERTERS; 

    static 
    { 
     CONVERTERS = new HashMap<>(); 
     CONVERTERS.put(Integer.class, new IntegerConverter()); 
    } 

    public static void main(String[] args) 
    { 
     Number valueA; 
     Number valueB; 

     valueA = CONVERTERS.get(Integer.class).convert("42"); 
     valueB = CONVERTERS.get(Integer.class).convert("Hello, World!"); 

     System.out.println(valueA); 
     System.out.println(valueB); 
    } 
} 

interface NumberConverter<T extends Number> 
{ 
    T convert(String str); 
} 

class IntegerConverter 
    implements NumberConverter<Integer> 
{ 
    @Override 
    public Integer convert(String str) 
    { 
     try 
     { 
      return Integer.valueOf(str); 
     } 
     catch (NumberFormatException ex) 
     { 
      return 0; 
     }  
    } 
} 
+2

Penso che un'API migliore sarebbe avere una chiamata come "NumberConvert.convert (Integer.class, string)" e gestire internamente i dettagli. –

0

Così, ho deciso per un approccio alternativo:

static String trimTo0(String str) { 
    if (str == null) return "0"; 
    str = str.trim(); 
    if (str.isEmpty()) return "0"; 
    return str; 
} 

Usage:

String value = null; 
System.out println("Double value: " + Double.parseDouble(trimTo0(value))); 

Si noti che questo è più limitata rispetto al metodo in questione, questo non converte non valida, non stringhe numeriche su "0". Per fare ciò, sarebbero necessari due metodi separati, uno a virgola decimale e un altro a supporto di interi.

0

Si può provare questo:

private <T> T convertToType(Class<T> clazz,String str) throws Exception { 
    return clazz.getConstructor(String.class).newInstance(str); 
} 

Qui è necessario considerare che il tipo deve avere un costruttore con un parametro String.