Я пытался создать модульный тест, который бы удостоверился, что список (или, в более общем смысле, контейнер ) содержит определенные обязательные элементы, при этом позволяя ему также содержать некоторые дополнительные необязательные элементы (но опять же из заранее определенного списка опций).
Предположим для определенности, что список:
- должен содержать элементы
foo
и bar
; - может содержать элемент
optional
; - не может содержать какие-либо другие элементы.
В Java, используя удобный satisfiesAnyOf()
из библиотеки AssertJ , этот тест можно записать следующим образом:
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class C {
@Test
public void t0() {
doTest(asList("foo", "bar"));
}
@Test
public void t1() {
doTest(asList("foo", "bar", "optional"));
}
private static void doTest(final List<String> items) {
assertThat(items).as("items").satisfiesAnyOf(
it -> assertThat(it).as("XXX").containsExactlyInAnyOrder("foo", "bar"),
it -> assertThat(it).as("YYY").containsExactlyInAnyOrder("foo", "bar", "optional"));
}
}
Проблема с этим кодом в том, что он компилируется только с использованием Java 8 компилятор, при этом Java 9 + компиляторы отклоняют его с помощью:
C.java:[26,63] no suitable method found for containsExactlyInAnyOrder(java.lang.String,java.lang.String)
method org.assertj.core.api.AbstractIterableAssert.containsExactlyInAnyOrder(capture#1 of ? extends java.lang.String...) is not applicable
(varargs mismatch; java.lang.String cannot be converted to capture#1 of ? extends java.lang.String)
method org.assertj.core.api.ListAssert.containsExactlyInAnyOrder(capture#1 of ? extends java.lang.String...) is not applicable
(varargs mismatch; java.lang.String cannot be converted to capture#1 of ? extends java.lang.String)
C.java:[27,63] no suitable method found for containsExactlyInAnyOrder(java.lang.String,java.lang.String,java.lang.String)
method org.assertj.core.api.AbstractIterableAssert.containsExactlyInAnyOrder(capture#2 of ? extends java.lang.String...) is not applicable
(varargs mismatch; java.lang.String cannot be converted to capture#2 of ? extends java.lang.String)
method org.assertj.core.api.ListAssert.containsExactlyInAnyOrder(capture#2 of ? extends java.lang.String...) is not applicable
(varargs mismatch; java.lang.String cannot be converted to capture#2 of ? extends java.lang.String)
Клиентский код может быть легко исправлен путем явного преобразования it
в каждой лямбде от List<? extends String>
до List<String>
, который даже не меняет сгенерированный байт-код:
assertThat(items).as("items").satisfiesAnyOf(
it -> assertThat((List<String>) it).as("XXX").containsExactlyInAnyOrder("foo", "bar"),
it -> assertThat((List<String>) it).as("YYY").containsExactlyInAnyOrder("foo", "bar", "optional"));
Глядя на AssertJ API, я не могу понять, как именно можно решить вышеуказанную проблему без небезопасное приведение.
Говоря в пользу Kotlin, эквивалентный Kotlin код компилируется нормально (тип it
может быть указан как eith эр MutableList<out String>
или List<String>
):
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class K {
@Test
fun t() {
val items = listOf("foo", "bar", "optional")
assertThat(items).`as`("items").satisfiesAnyOf({ assertThat(it).`as`("XXX").containsExactlyInAnyOrder("foo", "bar") },
{ assertThat(it).`as`("YYY").containsExactlyInAnyOrder("foo", "bar", "optional") })
}
}
Вопросы:
- Что именно изменилось в JLS между Java 8 и Java 9 ? Не могли бы вы указать мне точный раздел спецификации, запрещающий успешную компиляцию?
- Как можно переписать приведенный выше код Java, чтобы избежать явного приведения типов?