如何在 Java 中实例化泛型类型?

2022-09-04 20:58:59

我已使用 java.util.Properties 向我的应用添加了一个人类可读的配置文件,并尝试在其周围添加一个包装器,以使类型转换更容易。具体来说,我希望返回的值从提供的默认值“继承”其类型。以下是我到目前为止所得到的:

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

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

(完整的示例源。

然后,无论它是否从属性文件中读取,并且对于字符串,整数,双精度和&c,返回值都将是布尔值。当然,上面的代码段实际上并没有编译:getProperty("foo", true)

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

是我做错了,还是我只是想做一些做不到的事情?

编辑:用法示例:

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

答案 1

由于类型擦除,您无法实例化泛型对象。通常,您可以保留对表示该类型的对象的引用,并使用它来调用 newInstance()。但是,这仅适用于默认构造函数。由于您希望使用带有参数的构造函数,因此您需要查找构造函数对象并将其用于实例化:Class

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

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

        //try getting Constructor
        Constructor<T> constructor;
        try {
            constructor = clazz.getConstructor(new Class<?>[] { String.class });
        }
        catch (NoSuchMethodException nsme) {
            //handle constructor not being found
        }

        //try instantiating and returning
        try {
            return constructor.newInstance(value);
        }
        catch (InstantiationException ie) {
            //handle InstantiationException
        }
        catch (IllegalAccessException iae) {
            //handle IllegalAccessException
        }
        catch (InvocationTargetException ite) {
            //handle InvocationTargetException
        }
    }
}

但是,要了解实现这一目标需要多少麻烦,包括使用反射的性能成本,值得先研究其他方法。

如果您绝对需要采用此路由,并且如果仅限于编译时已知的一组不同的类型,则折衷方案是保持静态的s,该静态在启动时加载 - 这样您就不必在每次调用此方法时动态查找它们。例如,使用静态块填充的 a 或 。TMapConstructorMap<String, Constructor<?>>Map<Class<?>, Constructor<?>>


答案 2

这是你做不到的。

由于类型擦除,类型 虽然在编译时已知,但在运行时对 JVM 不可用。T

对于您的特定问题,我认为最合理的解决方案是为每个不同类型的手动编写代码:

protected String getProperty(String key, String fallback) { ... return new String(value); }
protected Double getProperty(String key, Double fallback) { ... return new Double(value); }
protected Boolean getProperty(String key, Boolean fallback) { ... return new Boolean(value); }
protected Integer getProperty(String key, Integer fallback) { ... return new Integer(value); }

笔记:

  • 在 Java 标准 API 中,您会发现许多地方存在一组相关方法,这些方法仅因输入类型而异。
  • 在C++,您可能可以通过模板来解决。但是C++引入了许多其他问题...

推荐