2013-04-16 26 views
7

Chciałbym stworzyć ogólną metodę, która działa skutecznie to:rodzajowych Java i typy liczbowe

class MyClass { 

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

Teraz powyżej nie skompilować z dwóch powodów: nie ma Number.valueOf() metoda i 0 nie może być oddane do T.

Przykład użycia:

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

wdraża powyżej metoda rodzajowa możliwie z Java? Jeśli tak, w jaki sposób? Jeśli nie, jakie jest dobre alternatywne podejście?


Edytuj: wydaje się, że istnieje kilka możliwych duplikatów, ale nie znalazłem jednego, który obejmuje dokładnie to. Mam nadzieję, że jest jakaś sztuczka do pobrania, która pozwoliłaby na te dwie operacje: łańcuch parsowania do podklasy Number i powrót do wartości 0 dla podklasy Number.

+1

['Numer'] (http://docs.oracle.com/javase/6/docs/api/java/lang/Numer.html) Klasa abstrakcyjna nie zawiera żadnej metody analizy łańcucha do typu, ponieważ przetwarzanie odbywa się według określonego typu. Sugestia polega na tym, aby nie używać generycznego, ale tworzyć funkcje takie jak "... ToInteger',' ... ToDouble' i tak dalej. –

+0

Nie sądzę, jest łatwy sposób to zrobić. Po prostu użyłbym czegoś takiego jak 'new MyClass (value) .asDouble();' i zaimplementuj metody 'asInteger',' asLong', 'asFloat',' asDouble' itd. Manualnie – assylias

+3

'T.valueOf()' wouldn ' t działa, nawet jeśli istnieje statyczna metoda 'Number.valueOf()'; po prostu nie można używać takich ogólnych typów. –

Odpowiedz

3

Zgadzam się w 100% z TofuBeer. Jednak w przypadku, gdy chcesz uniknąć ze względu na szczegółowość czasu, powinno to również zrobić:

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

} 

ale wyłącznie z T, nie można uzyskać typu w czasie wykonywania.

+0

To wydaje się być podejściem najbliższym temu, o co pytałem w pytaniu, chociaż nadal wolałbym, aby twoja odpowiedź obejmowała wychwytywanie wyjątków i zwracanie właściwej podklasy (więc zwróć 'T' i spróbuj' Klasy parametr, przypuszczam). – hyde

+0

Edytowano. Dzięki. – Jatin

3

Generics Java zapewnia jedynie kontrole czasu kompilacji, a informacje o typie są porzucane po kompilacji. Tak więc oświadczenie T.valueOf nie jest możliwe w Javie. Rozwiązaniem jest pójść w zwięzły sposób, jak już wspomniano w komentarzach. Czy istnieje jakiś powód, dla którego chcesz wykonać MyClass.<Double>sloppyParseNumber(value), ale nie MyClass.sloppyParseDouble(value), skoro i tak określasz typ podczas kompilacji?

+0

@hyde: Nie jestem pewien, czy rozumiem. Kiedy mówisz "jest to ta sama funkcja co zawsze, a określenie typu nic nie robi", dlaczego według ciebie określenie typu nic nie robi? W twoim zaktualizowanym przykładzie, jaki będzie typ zwracany 'sloppyParseNumber'? Skąd będzie wiedzieć, czy przekazana liczba powinna zostać przeanalizowana jako "BigDecimal',' Double', 'Float',' Integer' itd.? –

+0

Spróbujmy jeszcze raz: Cel 'MyClass. sloppyParseNumber' byłoby, aby metoda zwróciła podwójny obiekt, ale w rzeczywistości generics Java nie działa w ten sposób, metoda nie może znać typu określonego przez wywołującego w '<>'. – hyde

2

Metody statyczne są powiązane przez typ, ponieważ typ to w najlepszym razie Number i Number nie ma metody valueOf, której szukacie, nie będzie działać.

Najprostszym sposobem jest po prostu zrobić kilka metod statycznych jak sloppyParseInt, sloppyParseFloat, itp ...

Można zrobić coś takiego, nie wiem, mi się podoba, i pewnie można poprawić na:

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

Myślę, że lepszym API byłoby mieć połączenie typu 'NumberConvert.convert (Integer.class, string)' i obsługiwać szczegóły wewnętrznie. –

0

Więc zdecydowałem się na alternatywne podejście:

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

Zastosowanie:

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

pamiętać, że jest bardziej ograniczona niż metoda w pytaniu, to nie konwertuje nieważne, nie- ciągi numeryczne do "0". Wykonanie tego w pełni wymagałoby dwóch oddzielnych metod, z których jedna obsługuje kropkę dziesiętną, a druga obsługuje tylko liczby całkowite.

0

Można spróbować to:

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

Tutaj należy wziąć pod uwagę, że typ musi mieć konstruktor z parametrem String.

Powiązane problemy