簡體   English   中英

Java泛型-“ extends”關鍵字->不支持添加元素

[英]java generics - “extends” keyword -> not supporting add elements

由於Java泛型“ extend”關鍵字,我陷入了一個奇怪的問題。 我已經開發了一種通用方法,可以從一種通用方法中獲取元素。

當我使用<? extends X> <? extends X> ,我無法向其中添加任何元素。

就我而言,我正在使用通用模板來限制用戶提供的參數並提供返回類型ac。

class Root{

}

class Sub_1 extends Root{

}

class Sub_2 extends Root{

}

public static <T extends Root> List<T> getSubElements(Class<T> targetClass){

    List<T> ls=new ArrayList<>();

    if(targetClass.getSimpleName().equals("Sub_1")){
        Sub_1 sub_1 = new Sub_1();
        ls.add(sub_1);
    }else if(targetClass.getSimpleName().equals("Sub_2")){
        Sub_2 sub_2=new Sub_2();
        ls.add(sub_2);
    }

    return ls;
}

在上述情況下,將元素添加到列表時出現編譯錯誤。

ls.add(sub_1);

ls.add(sub_2);

解決這個問題現在看起來非常具有挑戰性。如果有人可以在這里提供一些提示,我將很高興。

謝謝!!

如果您可以接受任何從Root派生的類,並且所有類都具有默認構造函數...

public static <T extends Root> List<T> getSubElements(Class<T> targetClass) throws ReflectiveOperationException {
    List<T> ls = new ArrayList<>();
    T t = targetClass.getDeclaredConstructor().newInstance();
    ls.add(t);
    return ls;
}

...或在本地嘗試/捕獲異常。

您可以通過使調用者傳遞所需類型的Supplier (而不是該類型的Class ,而無需使用反射,而以類型安全的方式進行此操作。 然后, getSubElement代碼只需調用供應商即可獲取正確的實例:

static <T extends Root> List<T> getSubElements(Supplier<T> s) {
    List<T> ls = new ArrayList<>();
    ls.add(s.get());
    return ls;
}

調用者需要提供一種方法來創建其所需子類的實例。 這可能使用構造函數引用,也可能是對靜態工廠方法的引用。 如果類層次結構是這樣的:

public class Root { }

public class Sub1 extends Root {
    public Sub1() { ... }
}

public class Sub2 extends Root {
    public static Sub2 instance() { ... }
}

然后,調用者可以編寫如下代碼:

List<Sub1> list1 = getSubElements(Sub1::new);

List<Sub2> list2 = getSubElements(Sub2::instance);

綜上所述,這是一個經過驗證的有效實現,並使用在線java編譯器進行檢查:

import java.util.*;

class Root{

}

class Sub_1 extends Root{

}

class Sub_2 extends Root{

}

public class Bla  {

    public static <T extends Root> T factoryMethod(Class<T> targetClass) throws Exception{
        if (Sub_1.class ==(targetClass)) {
            return (T) (new Sub_1());
        }
        else if (Sub_2.class == (targetClass)) {
            return (T) (new Sub_2());
        }
        else {
            throw new Exception("Unsupported class type");
        }
    }

    public static List<Root> getSubElements() throws Exception{

        List<Root> ls=new ArrayList<>();
        ls.add(factoryMethod(Sub_1.class));
        ls.add(factoryMethod(Sub_2.class));

        return ls;
    }

    public static void main(String[] args) {
        try {
            List<Root> root = getSubElements();
            System.out.println(root);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我寧願說答案取決於邊界條件。 我們可以編寫代碼來獲得必要的功能,但它們可能/可能不遵守各種邊界條件,例如性能,安全性,完整性等。

例如:以下代碼

**T newInstance = targetClass.newInstance();**
**list.add(newInstance);**

也可以實現必要的功能,但可能/可能不遵守性能邊界,因為對反射方法的任何調用都會檢查安全性。

上面發布了帶有Supplier的解決方案也實現了類似的功能,但它可能不遵守安全邊界,因為惡意的Supplier可能會提供可能導致緩沖區溢出或DOS攻擊的值。 (如果您真的想嘗試一下,則可以創建一個靜態供應商並使用self進行初始化。您將得到堆棧溢出。java泛型書還提供了與安全性相關的類似示例,您可以參考該示例)。 我看到的問題在於當前情況下的java open子類型。

還有其他解決方案可以實現可能/可能不遵守這些邊界條件的所需功能(對創建的子類型的方式稍加修改)。 我看到的問題是由於開放式子類型系統引起的,這使得開發人員編寫遵循所有邊界條件的代碼變得冗長且困難。

該解決方案還完全取決於您的模型結構,無論您是創建數據的子類型coz還是基於行為,這在此簡短示例中可能都沒有體現。

您可以參考Philiph Wadler的Java Generics Book,以獲得有關我建議使用的泛型的更多詳細信息。 它還提供了以安全方式編寫代碼的重要方面。 有趣的是,您可以參考有關Java數據類的項目amber(java 11或更高版本),它試圖解決其中的一些問題。

暫無
暫無

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

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