簡體   English   中英

Java泛型中的捕獲可以在類型聲明中統一嗎?

[英]Can captures in Java generics be unified in type declarations?

考慮以下Java函數:

public void foo(Class<? extends Exception> cl, List<? extends Exception> ls) throws Exception {
    ls.add(cl.newInstance());
}

這是行不通的,因為clls的類型捕獲不是統一的,並且實際上可以引用不同的類型。 如果已編譯此函數,我可以將其稱為foo(NullPointerException.class, new List<SecurityException>()) ,這將是非法的。

很明顯,我們可以通過統一類型捕獲來解決此問題,如下所示:

public <T extends Exception> void foo(Class<T> cl, List<T> ls) throws Exception {
    ls.add(cl.newInstance());
}

現在,此功能可以按預期工作。 因此,我的問題是:有沒有辦法在單個類型聲明中統一類型捕獲?

例如,我經常發現自己想要一個將類映射到自身實例的映射。 我目前可以想到的唯一方法是擁有一個附帶功能,該功能可以進行未經檢查的轉換:

private Map<Class<? extends Foo>, ? extends Foo> map = ...;
@SuppressWarnings("unchecked")
private <T extends Foo> T getFoo(Class<T> cl) {
    return((T)map.get(cl));
}

但是,不必抑制警告,讓編譯器了解映射類型聲明中的兩個類型捕獲應該相同,然后公開映射,顯然會更好。 例如,如果我可以有一個與此類似的聲明:

<T extends Foo> Map<Class<T>, T> map = ...;

顯然,這不是有效的語法,但是我的問題可以歸結為:是否有任何有效的語法可以使我做到這一點?

您正在尋找一種高級的泛型 Java沒有它們,可能永遠也不會。 我意識到這是一個Java問題,不過我會指出Scala具有更高的種類。 如果您想了解在類型系統中擁有這種功能意味着什么,那么您可能想要使用它。

並非沒有抑制某些警告。

您真的不想通過在類型系統中將Map “特殊”來做到這一點- Function類型又是什么呢? 即使在高度高級的類型系統中,描述諸如“此類僅對應於此類的實例的類”之類的復雜關系也是非常困難的-我什至不確定Agda能夠做到這一點,而且Agda具有最強大的類型系統了解。

當然,您可以將其包裝在內部不安全,外部安全的API中。 番石榴的ClassToInstanceMap 但是您不能在編譯時做出這種保證。

關於什么

public static <T extends Exception> void foo(Class<T> cl, List<? super T> ls) throws Exception {
    ls.add(cl.newInstance());
}

也可以在有效Java中找到要點( 生產者擴展了Consumer Super

不,唯一的“統一”方法是使用類型變量,並且在引入它的整個聲明中,類型變量始終代表相同的類型。 由於無法將類型變量添加到Map.Entry ,因此無法引入表示地圖中每個條目的不同類型的類型變量。

我在另一個答案中描述了不可能有效地限制地圖的條目類型的可能性。

當然,如果您不必實現Map接口,而只想提供putget方法,則可以使用方法作用域類型變量來統一。 但是,只要您想要一個addAll()方法,就超越了Java類型系統的表現力。

我首先想到的是擴展Map接口的實現,並這樣聲明:

public class ClassMap<T extends Foo> extends HashMap<Class<T>, T>

盡管您似乎不想執行其他操作。

恕我直言,您遇到問題的原因是對泛型的理解和使用通配符。

您提出的第一個示例應適當措辭。

public void foo(Class<? extends Exception> cl, List<? extends Exception> ls) throws Exception;

與擁有相同

public void foo(Class<Integer> c1, List<String> ls) throws Exception

我想介紹的是,在這兩種情況下,參數沒有任何共同之處。

如果要強制這樣做,則兩個參數使用相同的通用參數,我們需要在本節中為通用參數聲明它。

public <T extends Exception> void foo(Class<T> cl, List<T> ls) throws Exception

好的,現在我們確保兩個參數都必須使用擴展Exception類的類型

此解決方案有一些限制,因此我們可以通過引入通配符來改進? super T ? super T ,女巫通常代表T或超類。

public <T extends Exception> void foo(Class<T> cl, List<? super T> ls) throws Exception

回到您的原始問題“是否有任何有效的語法可以使我做到這一點?”

要在字段和方法中使用相同的通用參數,其作用域應為class。

public class Foo<T extends Exception> { 

   private Map<Class<T>,T> map;

   public void T getFoo(Class<T> clazz) {
     return map.get(clazz);
   }

}

但是,如果您不希望類定義中使用泛型類型,則無法保證方法的泛型類型與字段的泛型類型相同。

因此,針對您的情況的最終解決方案是,如果您創建一個封裝的地圖,並使用適當的方法設置和檢索對象,那么抑制警告並不是一件壞事。 因為您以其他方式確保了類型的安全性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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