簡體   English   中英

如何在Java中實例化泛型類型?

[英]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); }

筆記:

  • 在Java標准API中,您會發現許多地方存在一組僅由輸入類型不同的相關方法。
  • 在C ++中,您可能可以通過模板解決。 但是C ++引入了許多其他問題......

以下使用功能接口

您可以更改方法簽名以使用提供的“解析器”:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM