Я пытался создать модульный тест, который бы удостоверился, что список (или, в более общем смысле, контейнер) содержит определенные обязательные элементы, позволяя ему также содержать некоторые дополнительные необязательные элементы (но, опять же, из заранее определенного списка параметров).
Предположим для определенности, что список:
- должен содержать элементы
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"));
Глядя на API AssertJ, я не могу понять, как именно можно решить указанную выше проблему без небезопасного приведения типов.
Говоря в пользу Kotlin, эквивалентный код Kotlin компилируется просто отлично (тип it
может быть указан как 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, чтобы избежать явного приведения типов?
(List<String> it) -> assertThat(...
, чем кастинг. - person Andy Turner   schedule 27.05.2020satisfiesAnyOf()
, должен бытьConsumer<List<? extends String>>
. - person Bass   schedule 27.05.2020