我使用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);
}
}
getProperty("foo", true)
的返回值将是一个布尔值,无论是从属性文件中读取还是类似于字符串,整数,双精度和&amp; c。当然,上面的代码段实际上并没有编译:
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);
}
答案 0 :(得分:13)
由于type erasure,您无法实例化通用对象。通常,您可以保留对表示该类型的Class
对象的引用,并使用它来调用newInstance()
。但是,这仅适用于默认构造函数。由于您要使用带参数的构造函数,因此您需要查找Constructor
对象并将其用于实例化:
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
}
}
}
然而,看到实现这一目标有多麻烦,包括使用反射的性能成本,首先要考虑其他方法。
如果你绝对需要采用这种方式,并且如果T
仅限于编译时已知的一组不同类型,那么妥协就是保持Map
Constructor
s,在启动时加载 - 这样你就不必在每次调用这个方法时动态查找它们。例如Map<String, Constructor<?>>
或Map<Class<?>, Constructor<?>>
,使用static block填充。
答案 1 :(得分:1)
泛型是使用Java中的类型擦除实现的。在英语术语中,大多数通用信息在编译时丢失,并且您无法在运行时知道T
的实际值。这意味着你根本无法实现泛型类型。
另一种解决方案是在运行时为您的类提供类型:
class Test<T> {
Class<T> klass;
Test(Class<T> klass) {
this.klass = klass;
}
public void test() {
klass.newInstance(); // With proper error handling
}
}
编辑:更接近您案例的新示例
static <T> T getProperty(String key, T fallback, Class<T> klass) {
// ...
if (value == null) {
return fallback;
}
return (T) klass.newInstance(); // With proper error handling
}
答案 2 :(得分:1)
这是你不能做的事情。
由于类型擦除,类型T
虽然在编译时已知,但在运行时JVM不可用。
对于您的具体问题,我认为最合理的解决方案是手动编写每种不同类型的代码:
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); }
注意:
答案 3 :(得分:0)
如果您想保留现有的方法签名,请执行此操作。
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;
public class Main
{
private final Properties properties;
public Main()
{
this.properties = new Properties();
this.properties.setProperty("int", "1");
this.properties.setProperty("double", "1.1");
}
public <T> T getProperty(final String key, final T fallback)
{
final String value = this.properties.getProperty(key);
if (value == null)
{
return fallback;
}
else
{
try
{
return (T) fallback.getClass().getConstructor(new Class<?>[] { String.class } ).newInstance(value);
}
catch (final InstantiationException e)
{
throw new RuntimeException(e);
}
catch (final IllegalAccessException e)
{
throw new RuntimeException(e);
}
catch (final InvocationTargetException e)
{
throw new RuntimeException(e);
}
catch (final NoSuchMethodException e)
{
throw new RuntimeException(e);
}
}
}
public static void main(final String[] args)
{
final Main m = new Main();
final Integer i = m.getProperty("int", new Integer("0"));
final Double d = m.getProperty("double", new Double("0"));
System.out.println(i);
System.out.println(d);
}
}
答案 4 :(得分:0)
您可以更改方法签名以使用提供的“解析器”:
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);
}
}
此外,为了提高效率,您可能希望将T fallback
替换为Supplier<? extends T> fallbackSupplier
,以防止在不需要时创建备用值:
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);
}
}
然后,您可以将method references和lambda expressions用作解析器和后备供应商,例如:
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);
}
此方法的优点是它不再局限于(可能不存在的)构造函数。
答案 5 :(得分:-1)
试试这个:
protected <T> T getProperty(String key, T fallback) {
String value = properties.getProperty(key);
if (value == null) {
return fallback;
} else {
Class FallbackType = fallback.getClass();
return (T)FallbackType.cast(value);
}
}