Справка по Java Generics: невозможно использовать «Object» в качестве аргумента для «? Extends Object» - PullRequest
3 голосов
/ 28 декабря 2010

У меня есть следующий код:

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.put и valueMapPart2.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, потому что Map предоставляется какой-то библиотекой.

Так почему же это не компилируется?Кроме того, если я попытаюсь привести valueMap к Map<String,Object>, компилятор выдаст мне предупреждение «Непроверенное преобразование».

Спасибо!

Ответы [ 3 ]

4 голосов
/ 28 декабря 2010

Если в библиотеке указано extends, то они явно запрещают put.Вы должны защищаться перед изменением, так как они могут вполне законно изменить свой тип возвращаемого значения, чтобы быть неизменным в новой версии.Если копирование дорого, то вы можете попробовать создать тип карты типа <String, Object>, который сначала запрашивает их карту, а затем запрашивает созданную вами карту, которая имеет ваши локальные модификации.

Если вы знаете, чтоих тип возврата является неизменным, и если вы являетесь его владельцем, аннотации @SuppressWarnings("unchecked") - это законный способ обойти предупреждение, но я бы дважды проверил эти предположения и подробно прокомментировал.против 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» вместо extendsкак в:

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. 
3 голосов
/ 28 декабря 2010

Насколько я знаю, ограниченные широкие карты , т.е. ? 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.

В этот метод можно передать только List из Number, но не List из Double, даже если Double является подклассом Number.

Универсальный широкоформатный код может использоваться здесь, чтобы сообщить компилятору 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», который может быть списком, списком, списком и т. Д. Добавление объекта 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) к его супер типу, например Number или Object.

0 голосов
/ 28 ноября 2013

Это действительно относится к старому объектно-ориентированному гоче. На первый взгляд может показаться, что «мешок яблок» является подклассом «пакета фруктов», но это не так. С объектно-ориентированным кодом вы всегда можете использовать подкласс вместо суперкласса (который называется принцип подстановки Лискова ). Мешок с яблоками ломает это, потому что он не примет апельсин, тогда как мешок с фруктами примет апельсин.

С точки зрения вопроса, Collection<?> может быть Collection<Object> (что примет ваш Double) или это может быть Collection<Integer> (что не будет).

...