Java: fingerprint before generics

This method uses generic methods at the method level that parses values ​​from a custom POJO, which is JXlistOfKeyValuePairs

exactly what it is. The only thing is that both keys and values ​​are in JXlistOfKeyValuePairs

: String

s.

This method wants to accept in addition to an instance of JXlistOfKeyValuePairs

a Class<T>

, which determines what data type should convert the values ​​(assume only Boolean

, Integer

and are possible Float

). It then outputs HashMap

with the specified type for the values ​​in its entries.

This is the code I have and it is clearly broken.

private <T extends Object>  Map<String, T>
    fromListOfKeyValuePairs(JXlistOfKeyValuePairs jxval, Class<T> clasz)
{
    Map<String, T> val = new HashMap<String, T>();
    List<Entry> jxents = jxval.getEntry();
    T value;
    String str;
    for (Entry jxent : jxents)
    {
        str = jxent.getValue();
        value = null;
        if (clasz.isAssignableFrom(Boolean.class))
        {
            value = (T)(Boolean.parseBoolean(str));
        } else if (clasz.isAssignableFrom(Integer.class))
        {
            value = (T)(Integer.parseInt(str));
        } else if (clasz.isAssignableFrom(Float.class))
        {
            value = (T)(Float.parseFloat(str));
        }
        else {
            logger.warn("Unsupported value type encountered in key-value pairs, continuing anyway: " +
                clasz.getName());
        }
        val.put(jxent.getKey(), value);
    }
    return val;
}

      

This is the bit I want to solve:

if (clasz.isAssignableFrom(Boolean.class))
{
    value = (T)(Boolean.parseBoolean(str));
} else if (clasz.isAssignableFrom(Integer.class))
{
    value = (T)(Integer.parseInt(str));
}

      

I get: Inconvertible types required: T found: Boolean

Also, if possible, I would like to be able to do this with more elegant code, avoiding the # isAssignableFrom class.

Any suggestions?


Sample method call:

Map<String, Boolean> foo = fromListOfKeyValuePairs(bar, Boolean.class);

      


Resolved thanks to both @ Chris Dolan and @polygenelubricants. The reason was that coercion became confusing when combined with autoboxing a primitive. Compiler warnings are excluded because the method parameter clasz

is of type Class<T>

, not just Class

or Class<?>

, so the method call cast

was typical.

Impl. SOLN :.

private <T extends Object> Map<String, T> fromListOfKeyValuePairs(
    JXlistOfKeyValuePairs jxval, Class<T> clasz)
{
    Map<String, T> val = new HashMap<String, T>();
    List<Entry> jxents = jxval.getEntry();
    T value;
    String str;
    for (Entry jxent : jxents)
    {
        str = jxent.getValue();
        value = null;
        if (clasz.isAssignableFrom(Boolean.class))
        {
            value = clasz.cast(Boolean.parseBoolean(str));
        }
        else if (clasz.isAssignableFrom(Integer.class))
        {
            value = clasz.cast(Integer.valueOf(Integer.parseInt(str)));
        }
        else if (clasz.isAssignableFrom(Float.class))
        {
            value = clasz.cast((Object)Float.parseFloat(str));
        }
        else
        {
            logger.warn("Unsupporteded value type encountered in key-value pairs, continuing anyway: " +
                clasz.getName());
        }
        val.put(jxent.getKey(), value);
    }
    return val;
}

      

+2


a source to share


3 answers


You can use the method Class<T>.cast

instead of making your own untested one (T)

.

    if (clasz.isAssignableFrom(Boolean.class)) {
        value = clasz.cast(Boolean.parseBoolean(str));
    } else if (clasz.isAssignableFrom(Integer.class)) {
        value = clasz.cast(Integer.parseInteger(str));
    } else if (clasz.isAssignableFrom(Float.class)) {
        value = clasz.cast(Float.parseFloat(str));
    }

      

No compiler warnings.


As for why the original code doesn't compile, it's because you are trying to cast primitive directly to an unknown reference type. Casting directly from primitive to reference type only works in very specific cases, and in all these cases the type must be known at compile time.

    Object o;

    o = (Integer) 42; // works! Boxing conversion!
    o = (Number) 42;  // works! Autoboxing then widening reference conversion!
    o = (Object) 42;  // works! Autoboxing then widening reference conversion!
    o = 42; // YES! This also compiles!!!

    o = (String) ((Object) 42); // compiles fine!
    // will throw ClassCastException at run-time

    o = (String) 42; // DOESN'T COMPILE!!!

      



The last line is analogous to your casting from a primitive directly to an unknown parameterized type T

(i.e. (T) Integer.parseInt(s)

), so it doesn't compile. It is true that you are trying to write your code in such a way that it is the T

correct type, but there is no way to confirm that at compile time as it T

could be any type at all.

the previous last line bypasses the compile-time error indirectly by casting the primitive to String

after it has already been converted to the reference type Object

. This is why it compiles, although of course it will throw at runtime ClassCastException

.

Here's a typical example with a parameterized type: it's a bit silly, but it repeats the problem of casting primitives directly to an unknown reference type:

<T> T f() {
    //return (T) 42; // DOESN'T COMPILE!!!
    return (T) (Integer) 42; // compiles with warning about unchecked cast
}

      

Links

+5


a source


Here's what you want:

    if (clasz.isAssignableFrom(Boolean.class))
    {
        value = (T)Boolean.valueOf(Boolean.parseBoolean(str));
    } else if (clasz.isAssignableFrom(Integer.class))
    {
        value = (T)Integer.valueOf(Integer.parseInt(str));
    } else if (clasz.isAssignableFrom(Float.class))
    {
        value = (T)Float.valueOf(Float.parseFloat(str));
    }

      



The problem was that your code was cheating on autoboxing, so the compiler didn't convert the primitive instance boolean

to boolean

automatically. I've included explicit conversions and voila. The code complains about unsupervised roles, but this is inevitable, so you will want @SuppressWarnings

+2


a source


The simplest solution is probably to just treat things, in particular the value variable and the map, as an object, and then have the final conversion to (T) when the method returns.

You will get a bunch of unverified warnings, but you will still get them ...

0


a source







All Articles