How can I instantiate a generic type in Java?

Viewed 11629

I've added a human-readable configuration file to my app using java.util.Properties and am trying to add a wrapper around it to make type conversions easier. Specifically, I want the returned value to "inherit" it's type from the provided default value. Here's what I've got so far:

protected <T> T getProperty(String key, T fallback) {
    String value = properties.getProperty(key);

    if (value == null) {
        return fallback;
    } else {
        return new T(value);
    }
}

(Full example source.)

The return value from getProperty("foo", true) would then be a boolean regardless of whether it was read from the properties file and similarly for strings, integers, doubles, &c. Of course, the above snippet doesn't actually compile:

PropertiesExample.java:35: unexpected type
found   : type parameter T
required: class
                        return new T(value);
                                   ^
1 error

Am I doing this wrong, or am I simply trying to do something which can't be done?

Edit: Usage example:

// I'm trying to simplify this...
protected void func1() {
    foobar = new Integer(properties.getProperty("foobar", "210"));
    foobaz = new Boolean(properties.getProperty("foobaz", "true"));
}

// ...into this...
protected void func2() {
    foobar = getProperty("foobar", 210);
    foobaz = getProperty("foobaz", true);
}
6 Answers

The following uses functional interfaces.

You could change the method signature to use a provided "parser":

protected <T> T getProperty(String key, T fallback, Function<String, ? extends T> parser) {
    String value = properties.getProperty(key);

    if (value == null) {
        return fallback;
    } else {
        return parser.apply(value);
    }
}

Also, for efficiency you might want to replace T fallback with Supplier<? extends T> fallbackSupplier to prevent having to create fallback values when they are not needed:

protected <T> T getProperty(String key, Supplier<? extends T> fallbackSupplier, Function<String, ? extends T> parser) {
    String value = properties.getProperty(key);

    if (value == null) {
        return fallbackSupplier.get();
    } else {
        return parser.apply(value);
    }
}

Then you can use method references and lambda expressions as parser and fallback supplier, e.g.:

protected void func2() {
    foobar = getProperty("foobar", () -> 210, Integer::valueOf);
    // Better create own parsing method which properly handles 
    // invalid boolean strings instead of using Boolean#valueOf
    foobaz = getProperty("foobaz", () -> true, Boolean::valueOf);

    // Imagine creation of `ExpensiveObject` is time-wise or 
    // computational expensive
    bar = getProperty("bar", ExpensiveObject::new, ExpensiveObject::parse);
}

The advantage of this approach is that it is not restricted to a (potentially non-existent) constructor anymore.

Related