Странная реализация tryAdvance в Spliterator.OfInt

Как это работает? Как может Consumer<? super Integer> быть брошенным на IntConsumer??

default boolean tryAdvance(Consumer<? super Integer> action) {
    if (action instanceof IntConsumer) {
        return tryAdvance((IntConsumer) action);
    }
    else {
        if (Tripwire.ENABLED)
            Tripwire.trip(getClass(),
                      "{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
        return tryAdvance((IntConsumer) action::accept);
    }
}

2 ответа

Решение

Ваш вопрос не совсем понятен, так как в размещенном коде есть два приведения типа.

Первый проверяет через instanceof будь то Consumer<? super Integer> также реализует IntConsumer и затем выполняет приведение обычного типа, предполагая, что класс, реализующий Consumer а также IntConsumer в то же время будет делать это с той же семантикой. Сравните с документацией:

Требования к реализации:

Если действие является экземпляром IntConsumer тогда он приведен к IntConsumer и перешел к tryAdvance(java.util.function.IntConsumer); в противном случае действие адаптируется к экземпляру IntConsumer, в боксе аргумент IntConsumer, а затем перешел к tryAdvance(java.util.function.IntConsumer),

Итак, первый instanceof Проверка и приведение типа являются первой частью контракта, избегая бокса, если action Аргумент реализует оба интерфейса.

Второй тип броска является частью описанной адаптации к боксу IntConsumer в то время как бокс подразумевается ссылкой на метод (IntConsumer) action::accept, Эта ссылка на метод относится к методу void accept(T t) (где T := ? super Integer) который может быть адаптирован к функции подписи IntConsumer как описано Брайаном Гетцем. Поскольку эта функциональная подпись не только выполняет подпись IntConsumer но также (конечно) подпись Consumer<? super Integer> приведение типа необходимо для устранения неоднозначности между перегруженными tryAdvance методы. Было бы ненужным при использовании эквивалентного лямбда-выражения

return tryAdvance((int i)->c.accept(i));

Приведение предоставляет целевой тип для привязанного метода action::accept, что эквивалентно лямбде x -> action.accept(x), IntConsumer Тип target вызывает адаптацию этой лямбды (которая предпочла бы принимать целое число) для принятия int (что приведет к неявному заключению аргумента в коробку перед передачей его в action.accept()).

Другие вопросы по тегам