Общий вопрос Java - PullRequest
       25

Общий вопрос Java

3 голосов
/ 12 мая 2011

Следующий код компилируется, но если я раскомментирую закомментированную строку, это не так, и я не понимаю, почему. HashMap действительно расширяет AbstractMap, и первая строка, где объявлена ​​карта, прекрасно компилируется.

import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String args[]) {
        Map<String, ? extends AbstractMap<String, String>> map = new HashMap<String, HashMap<String, String>>();
        //map.put("one", new HashMap<String, String>());
    }
}

И я знаю, что «правильный путь» таков:

import java.util.HashMap;
import java.util.Map;

public class Test {

    public static void main(String args[]) {
        Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
        map.put("one", new HashMap<String, String>());
    }
}

Ответы [ 3 ]

7 голосов
/ 12 мая 2011

Первый код небезопасен - представьте, что вы на самом деле написали:

HashMap<String, ConcurrentHashMap<String, String>> strongMap = 
    new HashMap<String, ConcurrentHashMap<String, String>>();
Map<String, ? extends AbstractMap<String, String>> map = strongMap;

Сейчас:

map.put("one", new HashMap<String, String>());
ConcurrentHashMap<String, String> x = strongMap.get("one");

Мы должны иметь ConcurrentHashMap -но в действительности у нас есть только HashMap.

Это на самом деле намного проще объяснить, если мы уменьшаем количество генериков ... Ваш сценарий действительно эквивалентен(скажем):

List<? extends Fruit> list = new List<Apple>();
list.add(new Apple());

, который выглядит нормально, пока вы не посчитаете, что он эквивалентен в действительности (что касается компилятора ):

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> list = apples;
list.add(new Orange());
Apple apple = list.get(0); // Should be okay... but element 0 is an Orange!

что, очевидно, не хорошо.Компилятор должен обращаться с ними одинаково, поэтому он делает их обоих недействительными.

0 голосов
/ 12 мая 2011

Если вам нужно действительно техническое объяснение, я бы порекомендовал прочитать эти слайды в программном обеспечении на основе компонентов , чтобы получить полную картину проблемы, поскольку ваша маленькая проблема имеет огромные последствия в больших масштабах :)

Реальные условия: контравариантность и ковариация . Ищите их в этих слайдах относительно ОО-дизайна и его ограничений в больших системах.

Ваша проблема в том, что они смешаны с некоторыми генериками , смешанными в:)

То есть, ваш контракт ( что-то, что является фруктом чего-то более специфичного, чем фрукт * ) указывает, что список должен содержать всех видов фруктов, но вы создаете список, способный содержать только определенный вид фруктов ( яблоки или что-то более конкретное, чем яблоко ), что, по моему мнению, должно вызывать ошибку компиляции, но Java-компилятор слишком хорош и Обобщения не реализованы должным образом в Java (семантически и с точки зрения самоанализа).

Экземпляр контейнера может быть ковариантным или инвариантным по отношению к типу / классу контейнера, но лицо, являющееся экземпляром, должно быть инвариантом по отношению к типу / классу контейнера.

Конкретный пример:

List<Fruit> list = new ArrayList<Fruit>();

Общий пример:

ConatainerType<ElementOfList> list = new MoreSpecificContainerType<ElementOfList>();

ElementOfList должен выполнять как ковариацию, так и контравариантность, поскольку объекты могут быть как помещены (ковариация), так и извлечены (контравариантность), что оставляет только инвариантность, то есть тот же тип / класс.

Это был действительно длинный ответ, но я надеюсь, что он поможет кому-то, кто задаст подобный вопрос в будущем.

0 голосов
/ 12 мая 2011

В дополнение к превосходному ответу Джона, см. Также этот вопрос о PECS , который охватывает много той же области.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...