Generics hell: hamcrest matcher как параметр метода

Итак, у нас есть список строк и функция, которая принимает сопоставитель Hamcrest и возвращает результат метода matches() предоставленного сопоставителя:

public boolean matchIt(final Matcher<? super List<String>> matcher) {
    final List<String> lst = obtainListFromSomewhere();
    return matcher.matches(lst);
}

Все идет нормально. Теперь я легко могу позвонить:

matchIt(empty());
matchIt(anything());
matchIt(hasItem("item"));
matchIt(everyItem(equalToIgnoringCase("item")));

... поскольку все эти фабричные статические методы производят сопоставление, которое соответствует сигнатуре метода Matcher<? super List<String>>.

Однако я считаю, что сопоставитель, который принимает Iterable объектов, также должен приниматься методом matchIt():

matchIt(everyItem(anything()));

Поэтому я наивно изменил подпись метода matchIt():

public boolean matchIt(final Matcher<? super List<? super String>> matcher);

Но это совсем не работает. Он не только не принимает everyItem(anything()), но даже не принимает ранее правильное everyItem(equalToIgnoringCase("item")) высказывание (версия компилятора 1.7.0_05):

actual argument Matcher<Iterable<String>> cannot be converted to Matcher<? super List<? super String>> by method invocation conversion

Что за? Так что здесь не так? Это сигнатура метода matchIt() или сигнатура everyItem() Hamcrest разработана неправильно? Или это просто не подлежит ремонту система дженериков Java? Большое спасибо за комментарии!

ИЗМЕНИТЬ @rlegendi, я намерен предоставить клиенту интерфейс для добавления и выполнения предикатов для списка. Это matchIt() метод. Вызов matchIt(anything()) имеет смысл в этом сценарии, поскольку клиент хочет знать, есть ли в списке что-нибудь. Вызов matchIt(empty()) означает, что клиент хочет узнать, пуст ли список. И наоборот для matchIt(everyItem(equalToIgnoringCase("item"))) и matchIt(hasItem("item")).

Моя цель здесь - получить лучшую matchIt() сигнатуру метода. Matcher<? super List<String>> отлично работает для всех предыдущих сценариев. Однако я считаю, что клиенту должно быть разрешено добавлять Matcher<Iterable<Object>> сопоставителей (например, matchIt(everyItem(notNullValue()) имеет здесь смысл, клиент хочет знать, не является ли каждый строковый элемент списка нулевым).

Однако я не могу найти нужную подпись, matchIt(Matcher<? super List<? super String>>) не работает для everyItem(notNullValue());

Пользуюсь Hamcrest 1.3.

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

Я считаю, что нашел свое коренное недоразумение.

Выражение everyItem(anything()) возвращает объект типа Matcher<Iterable<Object>>. Так что я могу легко Matcher<Iterable<Object>> m = everyItem(anything());

Однако я не понимаю, почему я не могу делать Matcher<? super List<? super String>> m1 = m;. Кажется, что Matcher<Iterable<Object>> не Matcher<? super List<? super String>>, но я не понимаю, почему.

Я даже не умею Matcher<? super List<?>> m1 = m;. Matcher<Iterable<Object>> это не Matcher<? super List<?>>? Почему?


person Jan Dudek    schedule 21.10.2012    source источник
comment
Отличный вопрос! Я не уверен в ваших намерениях, но разве простой Matcher<?> не решит все ваши проблемы? Кстати, не могли бы вы рассказать немного больше о том, какой матчер вы хотели бы создать? Вы хотите сопоставить довольно много вещей (Object, Collection<? extends Object>, Iterable<? super String> и т. Д.), Что немного странно для меня, возможно, более подробное описание может помочь нам лучше понять вашу проблему.   -  person rlegendi    schedule 22.10.2012
comment
addHi (Список ‹?› список) {list.add (привет); } ... Список ‹Integer› integerList = новый Список ‹Integer› (); addHi (список целых); // Ой.   -  person johv    schedule 22.10.2012
comment
@johv Да, вы не можете добавить элемент в коллекцию unknown type, но если вы посмотрите на вопрос, то с параметром matcher таких операций не было.   -  person rlegendi    schedule 22.10.2012
comment
@rlegendi Нет разницы между вызовом matcher.matches и list.add.   -  person johv    schedule 22.10.2012
comment
@johv: Я думаю, есть разница: match () не объявляется с параметром типа, он принимает объект (или это было раньше в Hamcrest 1.2.1). Таким образом, его можно вызвать, и не будет ошибки компиляции (например, в вашем примере я могу вызвать list.indexOf (...) в любое время, но не добавить (...)).   -  person rlegendi    schedule 22.10.2012
comment
@rlegendi Понятно! В этом есть смысл.   -  person johv    schedule 22.10.2012
comment
@rlegendi Я добавил больше описания своих намерений к вопросу   -  person Jan Dudek    schedule 26.10.2012
comment
Аналогичный вопрос о List<List<? super String>> stackoverflow.com / questions / 13289847 /   -  person Arend v. Reinersdorff    schedule 13.11.2012


Ответы (2)


Однако я считаю, что сопоставитель, который принимает Iterable объектов, также должен приниматься методом matchIt ().

Нет, это не так. Вместо Iterable давайте пока рассмотрим List. Итак, у вас есть Matcher<List<Object>>, а его метод matches принимает List<Object>. Теперь, это займет List<String>? Нет. И вы, наверное, уже знаете почему - потому что он может добавить в список объект типа Object.

Теперь я знаю, что при присвоении имени классу Matcher вы ожидаете, что метод matches будет доступен только для чтения, а не изменит предоставленный ему список. Однако гарантии этого нет. Если он действительно ничего не добавляет в список, правильным типом для сопоставителя будет Matcher<List<?>>, который (1) не позволяет методу matches добавлять что-либо в список, кроме null, а (2) позволяет методу matches возьмите список любого типа.

Я считаю, что ваша текущая подпись метода public boolean matchIt(final Matcher<? super List<String>> matcher) уже позволяет Matcher<List<?>> (или Matcher<Iterable<?>>).

person newacct    schedule 21.10.2012
comment
Я добавил дополнительные пояснения к вопросу. Я не понимаю, что Matcher<Iterable<Object>> не Matcher<? super List<? super String>> и, что еще хуже, Matcher<Iterable<Object>> не Matcher<? super List<?>>. - person Jan Dudek; 28.10.2012
comment
@JanDudek: Matcher<Iterable<Object>> не является Matcher<? super List<? super String>>, потому что Iterable<Object> не является супертипом List<? super String>. Something<Object> - это подтип из Something<?>, а не супертип. По сути, в этом ответе я говорю о том, что тип параметра метода, который у вас уже есть, настолько общий, насколько это возможно, и ваша неспособность передать определенные вещи объясняется тем, что эти вещи недостаточно типизированы. Ваш сопоставитель должен быть Matcher<Iterable<?>>, а не Matcher<Iterable<Object>> - person newacct; 01.11.2012

Что-то не так?

public boolean matchIt(final Matcher<? extends Iterable<String>> matcher);
person Bohemian♦    schedule 22.10.2012
comment
matcher является потребителем, поэтому должно быть super - person newacct; 22.10.2012
comment
К сожалению, это соответствует только everyItem(equalToIgnoringCase("item")) (то есть Iterable<String>). ? super Iterable<String> не соответствует everyItem(anything()) (то есть Iterable<Object>). - person rlegendi; 22.10.2012
comment
Вы правы, согласно Руководству по использованию подстановочных знаков matcher является переменной in и должен быть определен с ограниченным сверху подстановочным знаком с использованием ключевого слова extends. - person Eddie G.; 22.10.2012