Обобщения и подстановочные знаки с коллекциями в Java

В тестовом классе, использующем AssertJ, у меня есть код, похожий на следующий:

public void someTest() {
    assertThat(getNames()).has(sameNamesAs(getExpectedNames()));
    assertThat(getNames()).doesNotHave(sameNamesAs(getOtherNames()));
}

private List<String> getNames() {
    return null;
}
private List<String> getExpectedNames() {
    return null;
}
private List<String> getOtherNames() {
    return null;
}

private Condition<List<String>> sameNamesAs(List<String> rhs) {
    return new Condition<List<String>>("same names as " + rhs) {
        @Override
        public boolean matches(final List<String> lhs) {
            return lhs.containsAll(rhs) && rhs.containsAll(lhs);
        }
    };
}

Я получаю ошибку компиляции при вызовах has и doesNotHave:

has/doesNotHave
(org.assertj.core.api.Condition<? super java.util.List<? extends java.lang.String>>)
in AbstractListAssert cannot be applied
to
(org.assertj.core.api.Condition<java.util.List<java.lang.String>>).

Я новичок в Java и не понимаю проблемы: java.util.List является супертипом java.util.List, а java.lang.String расширяет java.lang.String, не так ли?


person Christophe Fuzier    schedule 13.12.2016    source источник


Ответы (1)


В вашем случае методы has и doesNotHave принимают условие Condition<? super List<? extends T>, а не Condition<? super List<T>>, поскольку вы возвращаетесь из своего метода Condition<List<T>> sameNamesAs.

Вам нужен экземпляр типа Condition<List<? extends String>> (это подкласс исходного типа Condition<? super List<? extends String>>):

private Condition<List<? extends String>> sameNamesAs(List<String> rhs) {
    return new Condition<List<? extends String>>("same names as " + rhs) { ... };
}

Я попытался проиллюстрировать это следующим фрагментом:

List<String> list = getNames();

// ELEMENT = String, ACTUAL = List<? extends ELEMENT>
ListAssert<String> assertThat = assertThat(list);

// by the signature, we have to pass Condition<? super ELEMENT> or Condition<? super ACTUAL>
// Condition<? super ACTUAL> = Condition<? super List<? extends String>>
Condition<List<? extends String>> condition = sameNamesAs(list);

// Condition<List<? extends String>> extends Condition<? super List<? extends String>>
assertThat.has(condition);
person Andrew Tobilko    schedule 13.12.2016
comment
Спасибо за решение и объяснение. Если я правильно понимаю, моя ошибка заключалась в том, что я полагал, что List<String> является супертипом List<? extends String>, в то время как эти два типа на самом деле не имеют отношения наследования. Я просто не понимаю эту строку в вашем ответе: // by the signature, we have to pass Condition<? super ELEMENT> or Condition<? super ACTUAL>. Что здесь ACTUAL? - person Christophe Fuzier; 13.12.2016
comment
@ChristopheFuzier, да, потому что List<String> дочерний элемент List<? extends String>. ACTUAL, ELEMENT - это просто разные общие параметры методов и класса Assertions соответственно (ACTUAL = List<? extends ELEMENT>) - person Andrew Tobilko; 13.12.2016
comment
Я не смог различить ключевые слова супер, когда они используются вместо расширений. В чем разница между Состояние‹? супер ЭЛЕМЕНТ› и Состояние‹? расширяет ЭЛЕМЕНТ›? Почему не используете последний? - person sidnc86; 24.12.2019