[英]How can I instantiate a generic type in Java?
我使用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)
的返回值將是一個布爾值,無論它是從屬性文件中讀取還是類似於字符串,整數,雙精度和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);
}
由於類型擦除 ,您無法實例化通用對象。 通常,您可以保持對表示該類型的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
被限制在編譯時已知的一組不同類型,那么妥協就是保持一個靜態的Constructor
Map
,它在啟動時加載 - 這樣你就不會每次調用此方法時都必須動態查找它們。 例如, Map<String, Constructor<?>>
或Map<Class<?>, Constructor<?>>
,它使用靜態塊填充。
泛型是使用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
}
這是你不能做的事情。
由於類型擦除,類型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); }
筆記:
以下使用功能接口 。
您可以更改方法簽名以使用提供的“解析器”:
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);
}
}
另外,為了提高效率,您可能希望用Supplier<? extends T> fallbackSupplier
替換T fallback
Supplier<? extends T> fallbackSupplier
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);
}
}
然后,您可以使用方法引用和lambda表達式作為解析器和回退供應商,例如:
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);
}
這種方法的優點是它不再局限於(可能不存在的)構造函數。
如果您想保留現有方法簽名,請執行此操作。
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);
}
}
嘗試這個:
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);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.