Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics and numeric types

Tags:

java

generics

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.

like image 428
hyde Avatar asked Apr 16 '13 06:04

hyde


3 Answers

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.

like image 126
Jatin Avatar answered Oct 17 '22 11:10

Jatin


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?

like image 3
Sanjay T. Sharma Avatar answered Oct 17 '22 11:10

Sanjay T. Sharma


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;
        }    
    }
}
like image 2
TofuBeer Avatar answered Oct 17 '22 10:10

TofuBeer