簡體   English   中英

幫助Java Generics:不能使用“Object”作為參數“? extends Object“

[英]Help with Java Generics: Cannot use “Object” as argument for “? extends Object”

我有以下代碼:

import java.util.*;

public class SellTransaction extends Transaction {
    private Map<String,? extends Object> origValueMap;
    public SellTransaction(Map<String,? extends Object> valueMap) {
        super(Transaction.Type.Sell);
        assignValues(valueMap);
        this.origValueMap=valueMap;
    }
    public SellTransaction[] splitTransaction(double splitAtQuantity) {
        Map<String,? extends Object> valueMapPart1=origValueMap;
        valueMapPart1.put(nameMappings[3],(Object)new Double(splitAtQuantity));
        Map<String,? extends Object> valueMapPart2=origValueMap;
        valueMapPart2.put(nameMappings[3],((Double)origValueMap.get(nameMappings[3]))-splitAtQuantity);
        return new SellTransaction[] {new SellTransaction(valueMapPart1),new SellTransaction(valueMapPart2)};
    }
}

當我調用valueMapPart1.putvalueMapPart2.put ,代碼無法編譯,錯誤如下:

The method put(String, capture#5-of ? extends Object) in the type Map is not applicable for the arguments (String, Object)

我已經在互聯網上閱讀了關於泛型和通配符以及捕獲的內容,但我仍然不明白出了什么問題。 我的理解是Map的值可以是擴展Object的任何類,我認為這可能是多余的,因為所有類都擴展了Object。 我無法將泛型更改為類似的東西? super Object ? super Object ,因為Map是由某些庫提供的。

那為什么不編譯? 此外,如果我嘗試將valueMapMap<String,Object> ,編譯器會向我提供“未經檢查的轉換”警告。

謝謝!

如果庫指定extends則它們明確禁止put 你應該在修改之前進行防御性復制,因為他們可以非常合理地將他們的返回類型更改為在新版本中不可變。 如果復制很昂貴,那么您可以嘗試創建一個類型為<String, Object>的地圖類型,該類型首先查詢其地圖,然后查詢您創建的具有本地修改的地圖。

如果您確實知道他們的返回類型是不可變的並且您只擁有它,那么@SuppressWarnings("unchecked")注釋是解決警告的合法方式,但我會仔細檢查這些假設並進行廣泛評論。

要了解extends vs super ,請以這種方式查看。 由於該值可以是擴展Object任何類型,因此以下內容有效。

Map<String, Number> strToNum = new HashMap<String, Number>();
strToNum.put("one", Integer.valueOf(1));  // OK

Map<String, String> strToStr = new HashMap<String, String>();
strToStr.put("one", "1");  // OK

Map<String, ? extends Object> strToUnk = randomBoolean() ? strToNum : strToStr;
strToUnk.put("null", null);  // OK.  null is an instance of every reference type.
strToUnk.put("two", Integer.valueOf(2));  // NOT OK.  strToUnk might be a string to string map
strToUnk.put("two", "2");  // NOT OK.  strToUnk might be a string to number map

所以put並不適用於extends邊界類型。 但它可以很好地與閱讀操作如get

Object value = strToUnk.get("one");  // We don't know whether value is Integer or String, but it is an object (or null).

如果您希望地圖主要使用“put”而不是“get”,那么您可以使用“super”而不是extend,如下所示:

Map<String, Number> strToNum = new HashMap<String, Number>();
Map<String, Object> strToObj = new HashMap<String, Object>();

Map<String, ? super Number> strToNumBase;
if (randomBoolean()) {
  strToNumBase = strToNum;
} else {
  strToNumBase = strToObj;
}

// OK.  We know that any subclass of Number can be used as values.
strToNumBase.put("two", Double.valueOf(2.0d));

// But now, gets don't work as well.
Number n = strToNumBase.get("one");  // NOT OK. 

據我所知, 有界寬的卡片 ,即? extends Number ? extends Number ,不用於變量或文件。 它通常用於方法的參數。

我們首先考慮一個沒有泛型類型的情況。

public void method(List<Number> list) {
}

示例用法:

method(new List<Double>()); // <-- Java compiler complains about this
method(new List<Number>()); // <-- Java compiler is happy with this.

即使DoubleNumber子類,也只能將List of Number而不是List of Double傳遞給此方法。

這里可以使用widecard generic來告訴java編譯器這個方法可以接受Number任何子類列表。

public void method(List<? extends Number> list) {
}

示例用法:

method(new List<Double>()); // <-- Java compiler is happy with this.
method(new List<Number>()); // <-- Java compiler is happy with this.

但是,您將無法再修改列表對象,例如

public void method(List<? extends Number> list) {
    list.add(new Double()); // this is not allowed
}

上面的列表現在具有“未知的Number子類型”類型,可以是List,List,List等。將Double對象添加到未知類型的列表當然是不安全的。 為了說明這一點,對method的調用是

method(new ArrayList<Integer>());

...
public void method(List<? extends Number> list) {
    // adding Double to Integer list does not make sense.
    list.add(new Double()); // compiler error
}

對於變量和字段 ,您通常不使用有界寬帶,您可以這樣做

private Map<String, Object> origValueMap;

...

Map<String, Object> valueMapPart1 = origValueMap;
valueMapPart1.put(nameMappings[3], new Double(splitAtQuantity));

注意:不需要將new Double(splitAtQuantity)轉換為其超類型,例如NumberObject

這真的是一個舊的面向對象的陷阱。 乍一看,似乎“一袋蘋果”是“水果袋”的子類,但事實並非如此。 使用面向對象的代碼,您始終可以使用子類代替超類(稱為Liskov替換原則 )。 一袋蘋果打破了這個,因為它接受橙色,而一袋水果接受橙色。

在問題的條款中, Collection<?> 可以Collection<Object> (它可以接受你的Double ),也可以是Collection<Integer> (不會)。

暫無
暫無

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

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