8

I'd like to create a generic method which does effectively this:

class MyClass {

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

Now above does not compile for two reasons: there's no Number.valueOf() method and 0 can't be cast to T.

Example usage:

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

Is implementing above generic method possible with Java? If yes, how? If no, what's a good alternative approach?


Edit: there seems to be a few possible duplicates, but I did not find one, which covers exactly this. I'm hoping there's some trick to pull, which would allow these two operations: parse string to a Number subclass, and return 0 value for a Number subclass.

4
  • 1
    The Number abstract class does not contain any method to parse string to a type, because the parsing is done by a specific type. A suggestion is to not use generic, but to create functions like ...ToInteger, ...ToDouble and so on. Commented Apr 16, 2013 at 6:34
  • I don't think there is an easy way to do it. I would simply use something like new MyClass(value).asDouble(); and implement the asInteger, asLong, asFloat, asDouble etc. methods manually Commented Apr 16, 2013 at 6:39
  • 3
    T.valueOf() wouldn't work even if there was a static method Number.valueOf(); you simply can't use generic types like this. Commented Apr 16, 2013 at 6:40
  • 2
    This probably seems to a duplicate of java generic String to <T> parser. Commented Apr 16, 2013 at 6:41

5 Answers 5

3

I agree 100% with TofuBeer. But in case you wish to avoid verbosity for time sake, this should also do:

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

}

But purely from T, you cannot get the type at runtime.

Sign up to request clarification or add additional context in comments.

1 Comment

This seems like the approach closest to what I asked in the question, though I'd still prefer your answer to include catching the exceptions and returning the right subclass (so return T and have Class<T> clas parameter, I guess).
3

Java generics only provide compile time checks and the type information is pretty much thrown away after compilation. So the statement T.valueOf isn't possible in Java. The solution is to go the verbose way as already mentioned in the comments. Also, is there any reason why you want to do MyClass.<Double>sloppyParseNumber(value) but not MyClass.sloppyParseDouble(value) since you are anyway specifying the type at compile time?

2 Comments

@hyde: I'm not sure I understand. When you say "it's the same function as always and specifying the type does nothing", why do you think specifying the type does nothing? In your updated example, what will be the return type of sloppyParseNumber? How will it know whether the passed in number should be parsed as a BigDecimal, Double, Float, Integer etc.?
Let's try again: Purpose of MyClass.<Double>sloppyParseNumber would have been to make the method return Double object, but indeed Java generics do not work that way, method can't know the type specified by the caller in <>.
2

Static methods are bound by the type, since the type is, at best, Number and Number doesn't have a valueOf method what you are after isn't going to work.

The easiest way is to just make a number of static methods like sloppyParseInt, sloppyParseFloat, etc...

You could do something like this, not sure I like it, and can probably be improved on:

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

1 Comment

I think a better API would be to have a call like NumberConvert.convert(Integer.class, string) and handle the details internally.
0

So, I decided on an alternative approach:

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

Note that this is more limited than the method in the question, this does not convert invalid, non-numeric strings to "0". Doing that fully would require two separate methods, one supporting decimal point and another just supporting integers.

Comments

0

You can try this:

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

Here you need to consider that the Type must have a constructor with a String parameter.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.