2013-04-16 15 views
7

Ich möchte eine generische Methode schaffen, das tut effektiv diese:Java Generics und numerischen Typen

class MyClass { 

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

Jetzt oben nicht aus zwei Gründen nicht kompiliert: Es gibt keine Number.valueOf() Methode und 0 nicht gegossen werden T.

Beispiel Nutzung:

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); 

ist über generische Methode möglich mit Java implementieren? Wenn ja, wie? Wenn nein, was ist ein guter alternativer Ansatz?


Edit: Es scheint einige mögliche Duplikate zu sein, aber ich habe ein nicht gefunden, was genau diese abdeckt. Ich hoffe, es gibt einen Trick zu ziehen, der diese beiden Operationen ermöglichen würde: Zeichenfolge an eine Number-Unterklasse analysieren und 0-Wert für eine Number-Unterklasse zurückgeben.

+1

Die ['Nummer'] (http://docs.oracle.com/javase/6/docs/api/java/lang/Number.html) abstract class enthält keine Methode, um einen String nach einem Typ zu analysieren, da die Analyse nach einem bestimmten Typ erfolgt. Ein Vorschlag ist, nicht generisch zu verwenden, sondern Funktionen wie '... ToInteger',' ... ToDouble' und so weiter zu erstellen. –

+0

Ich glaube nicht, dass es einen einfachen Weg dafür gibt. Ich würde einfach so etwas wie 'new MyClass (Wert) .asDouble() verwenden,' und Umsetzung des 'asInteger',' asLong', 'asFloat',' asDouble' usw. Methoden manuell – assylias

+3

'T.valueOf()' wouldn‘ t funktionieren, selbst wenn eine statische Methode 'Number.valueOf()' vorhanden war; Sie können einfach keine generischen Typen wie diese verwenden. –

Antwort

3

Ich stimme zu 100% mit TofuBeer überein. Aber falls Sie es wünschen willen zu vermeiden Ausführlichkeit für die Zeit, sollte dies auch tun:

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); 

} 

Aber rein von T, können Sie nicht den Typ zur Laufzeit bekommen.

+0

Dies scheint der Ansatz zu sein, der dem am nächsten kommt, was ich in der Frage gefragt habe, obwohl ich deine Antwort immer noch bevorzuge, um die Ausnahmen einzufangen und die richtige Unterklasse zurückzugeben (also 'T' zurück zu geben und' Class clas' -Parameter, denke ich). – hyde

+0

Bearbeitet. Vielen Dank. – Jatin

3

Java-Generics bieten nur Compile-Time-Prüfungen und die Typinformationen werden nach der Kompilierung praktisch weggeworfen. Daher ist die Anweisung T.valueOf in Java nicht möglich. Die Lösung ist, den ausführlichen Weg zu gehen, wie bereits in den Kommentaren erwähnt. Gibt es auch einen Grund, warum Sie MyClass.<Double>sloppyParseNumber(value), aber nicht MyClass.sloppyParseDouble(value) tun möchten, da Sie den Typ zur Kompilierungszeit angeben?

+0

@hyde: Ich bin mir nicht sicher, ob ich das verstehe. Wenn Sie sagen "es ist die gleiche Funktion wie immer und die Angabe des Typs tut nichts", warum denkst du, die Angabe des Typs tut nichts? Was ist in Ihrem aktualisierten Beispiel der Rückgabetyp von "sloppyParseNumber"? Wie wird es wissen, ob die übergebene Zahl als "BigDecimal", "Double", "Float", "Integer" usw. geparst werden soll? –

+0

Versuchen wir es noch einmal: Zweck von 'MyClass. sloppyParseNumber' wäre gewesen, die Methode Double-Objekt zurückzugeben, aber tatsächlich Java-Generics funktionieren nicht auf diese Weise, Methode kann den Typ nicht kennen, der vom Aufrufer in '<>' angegeben wird. – hyde

2

Statische Methoden sind durch den Typ gebunden, da der Typ bestenfalls Number und Number keine valueOf Methode hat, nach der Sie nicht arbeiten werden.

Der einfachste Weg ist, einfach eine Reihe von statischen Methoden wie sloppyParseInt zu machen, sloppyParseFloat, etc ...

Sie könnten etwas tun, nicht sicher, ob ich es mag, und kann vermutlich auf verbessert werden:

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

Ich denke, eine bessere API wäre, einen Aufruf wie 'NumberConvert.convert (Integer.class, string)' zu haben und die Details intern zu behandeln. –

0

Also, ich nach einem alternativen Ansatz entschieden:

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

Verbrauch:

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

Beachten Sie, dass diese geringer als das Verfahren in der Frage ist, bedeutet dies nicht ungültig konvertieren, nicht numerische Zeichenfolgen zu "0". Um dies vollständig zu tun, würden zwei getrennte Methoden benötigt, ein unterstützender Dezimalpunkt und ein anderer, der nur Ganzzahlen unterstützt.

0

Sie können dies versuchen:

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

Hier müssen Sie beachten, dass der Typ einen Konstruktor mit einem String-Parameter haben.