java Map & lt; K, V & gt; как объект? [Дубликат]

У меня была такая же ошибка при настройке eclipse.ini для использования JRE6. Оказывается, я вызвал эту ошибку, неправильно настроив eclipse на использование 64-разрядной JVM при запуске 32-разрядной версии eclipse 3.7.

Правильная настройка требовала, чтобы в eclipse.ini -vm argumument использовался «C: / Program Files (x86) /» вместо «C: / Program Files /».

Сделать убедитесь, что используемая версия JVM (32/64 бит) соответствует версии eclipse (32/64 бит).

366
задан h0ussni 1 January 2014 в 17:03
поделиться

11 ответов

Как упоминалось другими, причина, по которой get() и т. д. не является общей, потому что ключ для записи, которую вы извлекаете, не должен быть того же типа, что и объект, который вы передаете get(); спецификация метода требует только того, чтобы они были равны. Это следует из того, как метод equals() принимает параметр Object as, а не только тот же тип, что и объект.

Хотя может быть общепринято, что многие классы имеют equals() так, чтобы его объекты может быть только равным объектам своего класса, на Java есть много мест, где это не так. Например, в спецификации List.equals() указано, что два объекта List равны, если они оба являются списками и имеют одно и то же содержимое, даже если они являются разными реализациями List. Поэтому, возвращаясь к примеру в этом вопросе, согласно спецификации метода возможно иметь Map<ArrayList, Something> и для меня, чтобы вызвать get() с аргументом LinkedList как, и он должен получить ключ, который является список с тем же содержимым. Это было бы невозможно, если get() были родовыми и ограничивали свой тип аргумента.

239
ответ дан Dave Newton 15 August 2018 в 18:14
поделиться
  • 1
    Тогда почему V Get(K k) в C #? – user 14 September 2011 в 16:21
  • 2
    Вопрос в том, хотите ли вы называть m.get(linkedList), почему вы не определили тип m как Map<List,Something>? Я не могу думать о том, что вызывать m.get(HappensToBeEqual), не меняя тип Map, чтобы получить интерфейс, имеет смысл. – Elazar Leibovich 14 February 2012 в 14:07
  • 3
    Ничего себе, серьезный недостаток дизайна. Вы тоже не получаете предупреждения о компиляторе. Я согласен с Элазаром. Если это действительно полезно, и я часто сомневаюсь, что getByEquals (Object key) звучит более разумно ... – momo 26 September 2012 в 13:25
  • 4
    Так почему же Java не избавилась от всех типов? – Dog 20 May 2013 в 14:27
  • 5
    Это решение похоже на то, что оно было сделано на основе теоретической чистоты, а не практичности. Для большинства применений разработчики гораздо лучше увидели бы аргумент, ограниченный типом шаблона, чем неограниченный для поддержки крайних случаев, подобных тому, который был указан newacct в его ответе. Выход из шаблонов без шаблонов создает больше проблем, чем решает. – Sam Goldberg 17 December 2013 в 20:03

Обратная совместимость, я думаю. Map (или HashMap) по-прежнему необходимо поддерживать get(Object).

1
ответ дан Anton Gogolev 15 August 2018 в 18:14
поделиться
  • 1
    Но такой же аргумент можно было бы сделать для put (что ограничивает общие типы). Вы получаете обратную совместимость, используя необработанные типы. Дженерики - это «отказ». – Thilo 1 September 2011 в 11:02
  • 2
    Лично я считаю, что наиболее вероятной причиной такого дизайнерского решения является обратная совместимость. – geekdenz 7 May 2016 в 07:07

Причина в том, что сдерживание определяется equals и hashCode, которые являются методами на Object, и оба принимают параметр Object. Это был ранний недостаток дизайна в стандартных библиотеках Java.

Единственный способ иметь безопасные хеш-таблицы и равенство в Java - это избегать Object.equals и Object.hashCode и использовать общий заменитель. Функциональная Java поставляется с типами классов только для этой цели: Hash<A> и Equal<A> . Предоставляется оболочка для HashMap<K, V> , которая принимает Hash<K> и Equal<K> в своем конструкторе. Таким образом, методы get и contains этого класса принимают общий аргумент типа K.

Пример:

HashMap<String, Integer> h =
  new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);

h.add("one", 1);

h.get("one"); // All good

h.get(Integer.valueOf(1)); // Compiler error
5
ответ дан Apocalisp 15 August 2018 в 18:14
поделиться
  • 1
    Это само по себе не препятствует объявлению типа «get» как «V get (K key)», потому что «Object» всегда является предком K, поэтому «key.hashCode ()» все еще будет иметь силу. – finnw 21 September 2009 в 19:44
  • 2
    Хотя это не мешает, я думаю, это объясняет это. Если они переключили метод equals для принудительного равенства классов, они, конечно же, не могли сказать людям, что в базовом механизме поиска объекта на карте используются equals () и hashmap (), когда прототипы метода для этих методов несовместимы. – cgp 12 July 2012 в 16:02

Контракт выражается следующим образом:

Более формально, если эта карта содержит отображение из ключа k в значение v, такое что (ключ == null? k == null: key .equals (k)), то этот метод возвращает v; в противном случае он возвращает null. (Это может быть не более одного такого отображения.)

(мой акцент)

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

27
ответ дан Brian Agnew 15 August 2018 в 18:14
поделиться
  • 1
    Он также зависит от hashCode(). Без правильной реализации hashCode () хорошо выполненный equals() в этом случае бесполезен. – rudolfson 13 May 2009 в 12:51
  • 2
    Я предполагаю, что в принципе это позволит вам использовать легкий прокси для ключа, если воссоздать весь ключ непрактично - до тех пор, пока equals () и hashCode () будут правильно реализованы. – Bill Michell 13 May 2009 в 13:32
  • 3
    @rudolfson: Насколько мне известно, только хэш-карта полагается на хэш-код, чтобы найти правильный ведро. Например, TreeMap использует двоичное дерево поиска и не заботится о hashCode (). – Rob 13 May 2009 в 18:19
  • 4
    Строго говоря, get() не нужно брать аргумент типа Object, чтобы удовлетворить контакт. Представьте, что метод get был ограничен типом ключа K - контракт все равно был бы действительным. Конечно, использование, когда тип времени компиляции не был подклассом K, теперь не скомпилировался, но это не аннулирует контракт, поскольку контракты неявно обсуждают, что происходит, если код компилируется. – BeeOnRope 2 June 2016 в 23:09

Это применение закона Postel's, «быть консервативным в том, что вы делаете, быть либеральным в том, что вы принимаете от других».

Проверки равенства могут выполняться независимо от типа ; метод equals определен в классе Object и принимает любой параметр Object в качестве параметра. Таким образом, имеет смысл эквивалентность ключей и операции, основанные на эквивалентности ключей, для принятия любого типа Object.

Когда карта возвращает значения ключа, она сохраняет как можно больше информации о типе, используя ее параметр типа.

16
ответ дан erickson 15 August 2018 в 18:14
поделиться
  • 1
    Тогда почему V Get(K k) в C #? – user 14 September 2011 в 16:20
  • 2
    Это V Get(K k) в C #, потому что это также имеет смысл. Разница между подходами Java и .NET - это только тот, кто блокирует несоответствующие вещи. В C # это компилятор, в Java это коллекция. Я рвусь о несовместимых классах коллекции .NET время от времени, но Get() и Remove(), принимающие только подходящий тип, конечно, не позволяют вам случайно передать неправильное значение. – Wormbo 29 May 2012 в 12:33
  • 3
    Это неправильное применение Закона Постела. Будьте либеральны в том, что вы принимаете от других, но не слишком либеральны. Этот идиотский API означает, что вы не можете определить разницу между «не в коллекции», и «вы сделали ошибку статического ввода». Многие тысячи потерянных часов программиста могли быть предотвращены с получением: K - & gt; логическое значение. – Judge Mental 30 April 2013 в 00:19
  • 4
    Конечно, это должно было быть contains : K -> boolean. – Judge Mental 10 June 2014 в 16:41
  • 5

Сейчас мы делаем большой рефакторинг, и нам не хватало этого строго типизированного get (), чтобы проверить, что мы не пропустили какой-то get () со старым типом.

Но я нашел обходной / уродливый трюк для проверка времени компиляции: создать интерфейс карты с строго типизированным get, containsKey, удалить ... и поместить его в пакет java.util вашего проекта.

Вы получите ошибки компиляции только для вызова get (),. .. с неправильными типами, все остальные выглядят нормально для компилятора (по крайней мере внутри eclipse kepler).

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

0
ответ дан henva 15 August 2018 в 18:14
поделиться

Существует еще одна веская причина: это невозможно сделать технически, потому что оно вызывает карту.

Java имеет полиморфную общую конструкцию, такую ​​как <? extends SomeClass>. Помеченная такая ссылка может указывать на тип, подписанный с помощью <AnySubclassOfSomeClass>. Но полиморфный родословный делает эту ссылку только для чтения. Компилятор позволяет использовать общие типы только как возвращающий тип метода (например, простые геттеры), но блокирует использование методов, когда общий тип является аргументом (например, обычные сеттеры). Это означает, что если вы пишете Map<? extends KeyType, ValueType>, компилятор не позволяет вам вызывать метод get(<? extends KeyType>), и карта будет бесполезной. Единственное решение - сделать этот метод не общим: get(Object).

4
ответ дан Holger 15 August 2018 в 18:14
поделиться
  • 1
    почему метод набора строго введен? – Sentenza 24 May 2014 в 18:19
  • 2
    если вы имеете в виду «put»: метод put () меняет карту, и он не будет доступен с помощью дженериков типа & lt ;? extends SomeClass & gt ;. Если вы это называете, вы получите исключение компиляции. Такая карта будет «только для чтения». – Owheee 17 June 2014 в 14:57

Удивительный Java-кодер в Google, Кевин Бурриллион, писал об этой проблеме в блоге некоторое время назад (по общему признанию, в контексте Set вместо Map). Наиболее релевантное предложение:

Равномерно методы Framework Java Collections Framework (и библиотеки коллекций Google) никогда не ограничивают типы их параметров, кроме случаев, когда необходимо предотвратить сбои коллекции.

Я не совсем уверен, что согласен с ним в принципе: .NET, похоже, требует правильного типа ключа, например - но стоит ли рассуждать в блоге , (Обозначив .NET, стоит пояснить, что часть причины, по которой это не проблема в .NET, заключается в том, что проблема с больше в .NET имеет более ограниченную дисперсию ...)

97
ответ дан Jon Skeet 15 August 2018 в 18:14
поделиться
  • 1
    Я уверен, что Джош Блох где-то написал об этом. Ранняя попытка использовала общий параметр для параметра, но оказалась слишком неудобной. – Tom Hawtin - tackline 13 May 2009 в 13:19
  • 2
    Apocalisp: это неправда, ситуация все та же. – Kevin Bourrillion 4 November 2009 в 17:24
  • 3
    @ user102008 Нет, сообщение не ошибается. Несмотря на то, что Integer и Double никогда не могут быть равны друг другу, все равно остается справедливым вопрос о том, содержит ли Set<? extends Number> значение new Integer(5). – Kevin Bourrillion 19 June 2012 в 16:10
  • 4
    Я никогда не хотел проверять членство в Set<? extends Foo>. Я очень часто менял тип ключа карты, а затем был разочарован тем, что компилятор не смог найти все места, где необходимо было обновить код. Я действительно не уверен, что это правильный компромисс. – Porculus 21 September 2012 в 21:52
  • 5
    @EarthEngine: Он всегда был сломан. В этом весь смысл - код сломан, но компилятор не может его поймать. – Jon Skeet 20 March 2013 в 08:46
  • 6

Совместимость.

До того, как появились дженерики, было только что (Object o).

Если бы они изменили этот метод, чтобы получить (& lt; K> o), он будет иметь потенциально принудительное массовое обслуживание кода для пользователей java, чтобы снова скомпилировать рабочий код.

Они могли ввели дополнительный метод , например get_checked (& lt; K> o) и обесценить старый метод get (), чтобы существовал более мягкий путь перехода. Но почему-то это не было сделано. (Ситуация, в которой мы сейчас находимся, заключается в том, что вам необходимо установить такие инструменты, как findBugs, для проверки совместимости типов между аргументом get () и объявленным типом ключа & lt; K> на карте.)

Аргументы Я думаю, что использование семантики .equals () является фиктивным. (Технически они верны, но я все еще думаю, что они фиктивные. Ни один дизайнер в здравом уме никогда не сделает o1.equals (o2) истинным, если o1 и o2 не имеют общего суперкласса.)

1
ответ дан Mr Lister 15 August 2018 в 18:14
поделиться

Я смотрел на это и думал, почему они это сделали. Я не думаю, что какие-либо из существующих ответов объясняют, почему они не могли просто заставить новый общий интерфейс принять только правильный тип для ключа. Фактическая причина заключается в том, что, хотя они и вводили дженерики, они НЕ создавали новый интерфейс. Интерфейс карты - это тот же самый старый универсальный Map, который просто служит как универсальной, так и не универсальной версией. Таким образом, если у вас есть метод, который принимает не общие карты, вы можете передать ему Map<String, Customer>, и он все равно будет работать. В то же время контракт на получение принимает объект, поэтому новый интерфейс также должен поддерживать этот контракт.

По моему мнению, они должны были добавить новый интерфейс и реализовать как на существующую коллекцию, но они решили в пользу совместимости интерфейсов, даже если это означает худший дизайн для метода get. Обратите внимание, что сами коллекции будут совместимы с существующими методами, только интерфейсы не будут.

1
ответ дан Stilgar 15 August 2018 в 18:14
поделиться

Я думаю, что в этом разделе учебника Generics объясняется ситуация (мой акцент):

«Вам нужно убедиться, что общий API не является чрезмерно ограничительным, он должен продолжаться для поддержки первоначального контракта API. Рассмотрим еще несколько примеров из java.util.Collection. Пред-общий API выглядит так:

interface Collection { 
  public boolean containsAll(Collection c);
  ...
}

Наивная попытка его генерировать:

interface Collection<E> { 
  public boolean containsAll(Collection<E> c);
  ...
}

Хотя это, безусловно, безопасный тип, он не соответствует оригинальному контракту API. Метод containsAll () работает с любым входящим набором. Он будет успешным только в том случае, если входящая коллекция действительно содержит только экземпляры из E, но:

  • Статический тип входящей коллекции может отличаться, возможно, потому, что вызывающий объект не знает точный тип передаваемой коллекции или, возможно, потому, что это коллекция & lt; ; S & gt ;, где S - подтип E.
  • . Совершенно законно вызывать containsAll () с набором другого типа. Процедура должна работать, возвращает false. "
11
ответ дан Yardena 15 August 2018 в 18:14
поделиться
  • 1
    почему бы не containsAll( Collection< ? extends E > c ), тогда? – Judge Mental 30 April 2013 в 00:20
  • 2
    @JudgeMental, хотя и не указан в качестве примера выше, также необходимо разрешить containsAll с Collection<S>, где S является супертипом E. Это было бы недопустимо, если бы это было containsAll( Collection< ? extends E > c ). Кроме того, поскольку is явно указано в примере, законно передавать коллекцию другого типа (с возвращаемым значением, а затем false). – davmac 10 June 2014 в 13:13
  • 3
    Нет необходимости разрешать containsAll с коллекцией супертипа E. Я утверждаю, что необходимо запретить этот вызов с проверкой статического типа, чтобы предотвратить ошибку. Это глупый контракт, который, на мой взгляд, является вопросом первоначального вопроса. – Judge Mental 10 June 2014 в 16:51
Другие вопросы по тегам:

Похожие вопросы: