2013-04-16 2 views
7

Я хотел бы создать общий метод, который делает фактически это:Java дженериков и числовые типы

class MyClass { 

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

Теперь выше не компилируется по двум причинам: нет Number.valueOf() метод и 0 не может быть приведен к T.

Пример использования:

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

реализует выше общего способа возможно с Java? Если да, то как? Если нет, то какой хороший альтернативный подход?


Редактировать: похоже, существует несколько возможных дубликатов, но я не нашел один, который охватывает именно это. Я надеюсь, что есть какой-то трюк, который позволил бы этим двум операциям: разбить строку на подкласс Number и вернуть значение 0 для подкласса Number.

+1

['Number'] (http://docs.oracle.com/javase/6/docs/api/java/lang/Number.html) абстрактный класс не содержит никакого метода для синтаксического анализа строки для типа, поскольку синтаксический анализ выполняется определенным типом. Предложение состоит в том, чтобы не использовать общий, но создавать такие функции, как '... ToInteger',' ... ToDouble' и так далее. –

+0

Я не думаю, что есть простой способ сделать это. Я просто использовал бы что-то вроде 'new MyClass (value) .asDouble();' и реализовал методы 'asInteger',' asLong', 'asFloat',' asDouble' и т. Д. Вручную – assylias

+3

'T.valueOf()' wouldn ' t, даже если существует статический метод 'Number.valueOf()'; вы просто не можете использовать такие общие типы. –

ответ

3

Я согласен на 100% с TofuBeer. Но в случае, если вы хотите, чтобы избежать многословия ради времени, это также должно сделать:

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

} 

Но чисто из T, вы не можете получить тип во время выполнения.

+0

Это похоже на подход, наиболее близкий к тому, что я задавал в вопросе, хотя я бы предпочел, чтобы ваш ответ включал в себя перехватывание исключений и возврат правильного подкласса (так что возвращаем 'T' и имеем параметр класса clas', я думаю). – hyde

+0

Отредактировано. Благодарю. – Jatin

3

Java-генераторы предоставляют только проверки времени компиляции, и информация о типе информации в значительной степени выбрасывается после компиляции. Таким образом, заявление T.valueOf невозможно в Java. Решение состоит в том, чтобы идти подробным способом, как уже упоминалось в комментариях. Кроме того, есть ли какая-либо причина, почему вы хотите сделать MyClass.<Double>sloppyParseNumber(value), но не MyClass.sloppyParseDouble(value), так как вы все равно указываете тип во время компиляции?

+0

@hyde: Я не уверен, что понимаю. Когда вы говорите: «Это такая же функция, что и всегда, и указание типа ничего не делает», почему вы думаете, что указание типа ничего не делает? В вашем обновленном примере, каким будет тип возврата 'sloppyParseNumber'? Как он узнает, следует ли анализировать прошедший номер как «BigDecimal», «Double», «Float», «Integer» и т. Д.? –

+0

Давайте попробуем еще раз: Цель 'MyClass. sloppyParseNumber' должен был сделать метод возвратом двоимого объекта, но в самом деле дженерики Java не работают таким образом, метод не может знать тип, указанный вызывающим в '<>'. – hyde

2

Статические методы привязаны по типу, поскольку тип в лучшем случае Number и Number не имеет способа valueOf, что вы после этого не собираетесь работать.

Самый простой способ это просто сделать ряд статических методов, как sloppyParseInt, sloppyParseFloat и т.д ...

Вы могли бы сделать что-то вроде этого, не уверен, что мне это нравится, и, вероятно, может быть улучшен на:

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

Я думаю, что лучшим API будет иметь вызов типа 'NumberConvert.convert (Integer.class, string)' и обрабатывать детали внутри. –

0

Итак, я решил на альтернативный подход:

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

Использование:

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

Обратите внимание, что это является более ограниченным, чем метод в этом вопросе, это не преобразует недействителен, не- числовые строки до "0". Для этого потребуется два разных метода, один из которых поддерживает десятичную точку, а другой - только целые числа.

0

Вы можете попробовать это:

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

Здесь нужно учитывать, что тип должен иметь конструктор с параметром String.

Смежные вопросы