Mnementh
Mnementh

Reputation: 51311

How to determine type in a generic method in Java?

I want a method that extracts the data from a JSON-object parsed before as the correct type. The JSONObject (rawdata) extends Map, so it looks like this:

private <Type> Type getValue(String key, Type def)
{
    if (!rawdata.containsKey(key)) return def;
    if (!(rawdata.get(key) instanceof Type)) return def;
    return (Type) rawdata.get(key);
}

The instanceof obviously generates a compile-time-error. The parameter def is the default-value, returned if the key is not available or has the wrong type. But def can also be null, so def.getClass() isn't working.

Any ideas how I can check the content of the Map-entry for the correct type?

Upvotes: 1

Views: 4631

Answers (4)

Andrejs
Andrejs

Reputation: 27677

Or you could use the Typesafe Heterogeneous Container (THC) pattern from "Effective Java Second Edition" book by Joshua Bloch.

Basically, store the item's Class in the map when inserting. When retrieving you'll know the type is the same.

Map<Class, Map<String, Object>> rawData = ...

Upvotes: 0

Dunes
Dunes

Reputation: 40693

You just need to check for nulls (a null will count as any type, if this is undesired behaviour then you will also need to pass in the desired class).

private <T> T getValue(String key, T def)
{
    if (!rawdata.containsKey(key)) return def;

    Object value = rawdata.get(key);

    if (def == null) return (T) value; 
    // note that the above is inherently unsafe as we cannot 
    // guarantee that value is of type T

    // this if statement is the same as "value instanceOf Type"
    // is type safe, but not null safe
    if (def.getClass().isAssignableFrom(value.getClass())) {
        return (T) value;
    } else {
        return def;
    }
}

A safer method signature would be:

private <T> T getValue(String key, T defaultValue, Class<T> defaultClass)

This way we can safely check the types match even when the default is null.

Upvotes: 2

takteek
takteek

Reputation: 7110

Your best bet is probably to accept a Class object for the return type in the case when no default value is given. You can just overload the function like:

private <T> T getValue(String key, Type defaultValue);

private <T> T getValue(String key, Class<T> type);

Upvotes: 1

Michael Borgwardt
Michael Borgwardt

Reputation: 346300

Due to type erasure, the only way to handle the case where the default value can be null is to have the method require an additional parmeter of type Class - which is generally better because it allows the default value to be a subclass of the required type.

Upvotes: 8

Related Questions