Как использовать интерфейс в качестве ключа карты - PullRequest
3 голосов
/ 26 ноября 2009

Я ищу помощь по вопросу использования интерфейса в качестве ключа карты. Я пытался реализовать решение и не получал ошибок времени компиляции, а только ошибки времени выполнения при выполнении интеграционных тестов Разве невозможно использовать интерфейс в качестве ключа или в моих тестах что-то не так?

Мой код выглядит примерно так

private Map<AInterface, Values> myMap = new HashMap<AInterface, Values>();

После получения набора ключей из myMap они содержат объекты с ожидаемым Id, но сравниваются с неравными. Таким образом, при использовании myMap.get (ключ объекта) я получаю нулевое значение, даже если объект с таким же идентификатором есть. При использовании конкретного класса вместо интерфейса все тесты проходят:

private Map<AClass, Values> myMap = new HashMap<AClass, Values>();

Я прочитал Обобщение , где говорится, что для карты необходимо заменить переменные типа K и V конкретными типами, которые являются подтипами объекта.

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

Есть ли у кого-нибудь опыт использования интерфейсов в качестве ключа на карте? А могли бы вы мне подсказать, что я делаю не так?

Ответы [ 6 ]

5 голосов
/ 26 ноября 2009

Ваши классы должны реализовывать hashCode и equals ( объяснение ; вам также следует ознакомиться с контрактом интерфейса карты ).

2 голосов
/ 26 ноября 2009

Все объекты, расширяющие ваш интерфейс, должны реализовывать как hashCode, так и equals. Если equals возвращает true, но значения hashCode не равны, то соответствующий объект не найден, поскольку JVM помещает объекты в «корзины» (при сохранении в Map) в соответствии с их hashCode значение.

0 голосов
/ 26 ноября 2009

Если вы используете Apache Commons lang JAR, возможно, добавьте это в свой класс.

public int hashCode() { 
    return HashCodeBuilder.reflectionHashCode(this);
}
0 голосов
/ 26 ноября 2009

Проблема не в интерфейсах. Универсальные типы стираются при компиляции, а во время выполнения HashMap работает только с экземплярами Object.

Мы не можем узнать, в чем ваша проблема, поскольку вы не показываете никакого кода, но, скорее всего, ваши ключи изменчивы, а ваш хэш-код меняется.

Например:

class MyKey {
    String name;

    public int hashCode() {
        return name.hashCode();
    }

    // assume suitable equals() implementation
}

Map<MyKey,Integer> myMap = new HashMap();

MyKey key1 = new MyKey();
key1.name = "Jimmy";

myMap.put(key1, 10);

key1.name = "Johnny";

myMap.get(key1);  // return null

Если хеш-код объекта изменяется между его добавлением на карту и попыткой его получения, то HashMap будет (вероятно) искать в неправильном хеш-хранилище и не найдет никакого значения. Иногда вы можете получить результат, если старый хеш-код и новый хеш-код разрешаются в одно и то же хэш-поле.

0 голосов
/ 26 ноября 2009

[...] говорится, что для Карты вы требуется заменить тип переменные K и V по конкретным типам которые являются подтипами объекта.

Я был близок к тому, чтобы задать вопрос в SO, но я думаю, что получил ответ.

конкретный тип - Это звучит как «конкретный класс», как будто интерфейсы или абстрактные классы не допускаются. Но это просто говорит о том, что вы не можете заменить «K» другим общим типом «S» или около того. Это должен быть «настоящий» тип: интерфейсы, классы, даже перечисления достаточно конкретны.

подтипы Object - Опять же, похоже, что интерфейсы не разрешены, потому что они не подклассируют Object. Да, но: вы все равно не можете создать экземпляр интерфейса, поэтому реальные объекты, помещенные в карту, - это всегда экземпляры класса. Единственный класс типов Java, который не подкласс класса Object, это примитивы Java , включая массивы примитивов Java . Так что Map<int, String> не допускается , а также Map<String, int[]>.

0 голосов
/ 26 ноября 2009

Оба примера должны работать отлично. В «дженериках» можно использовать интерфейсы, абстрактные или конкретные типы классов. Я часто использую интерфейс Список в Картах, и у меня никогда не было проблем.

Вы говорите, что вам нужно только изменить тип myMap и конструктор, чтобы ваши тесты прошли? Какие типы объектов вы используете в качестве ключей на карте, AClass или что-то еще?

Внимательно посмотрите на ваши ошибки во время выполнения или предоставьте нам некоторые детали (только исключения без трассировки стека).

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

EDIT

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

РЕДАКТИРОВАТЬ 2

Если вы получаете NPE непосредственно на myMap.get(AClass key), тогда либо myMap - это null, либо ключ (но это все еще не решает другую загадку ...)

РЕДАКТИРОВАТЬ 3

Только что проверил реализацию хеш-кода и равно на UUID, и это нормально. Вычисление ограничено только 128-битным UUID. Таким образом, если вы создаете два объекта UUID для одного и того же значения UUID и проверяете на равенство, тогда два объекта UUID не совпадают, а равны. Это хорошо. Если у вас есть метод получения UUID в AClass, то вы экспериментируете с картой типа HashMap<UUID, Values> myMap и проверяете, работает ли она по-прежнему (может быть, случайно, изменение кода показывает настоящую ошибку;))

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