Java Лямбда-функция кормления с расширенным типом - PullRequest
0 голосов
/ 20 апреля 2020

Eclipse говорит мне, что это:

interface Recursive
{
    List<? extends Recursive> getSubitems();

    default void processRecursive(Consumer<? extends Recursive> f)
    {
        f.accept(this);
        for (Recursive r : getSubitems())
            f.accept(r);
    }
}

невозможно (ошибка для f.accept (this) и f.accept (r): метод accept (capture # 1-of? Extends) Рекурсивный) в типе Consumer не применим для аргументов (Рекурсивный))

Мне интересно, почему, поскольку это расширяет Рекурсивный, то будет и r (рекурсивный), и они могут быть обработаны как Рекурсивные ..

Мне кажется, это было бы совершенно разумным и предсказуемым поведением.

1 Ответ

4 голосов
/ 20 апреля 2020

Допустим, у вас есть два класса, реализующих интерфейс: Foo и Bar

Consumer<Bar> barConsumer = ...

Foo foo = new Foo();
foo.processRecursive(barConsumer);

Теперь, согласно сигнатуре метода, этот вызов processRecursive() действителен, потому что barConsumer подходит Consumer<? extends Recursive>.

Однако код f.accept(this) нарушит Consumer<Bar> при попытке отправить объект Foo в Comsumer. Вот почему компилятор жалуется, потому что он не может гарантировать безопасность типов, то есть, что методу accept() передаются только действительные объекты.

Поскольку нет способа обеспечить, чтобы this был правильным подтипом Recursive, нет типобезопасного способа сделать это.

Если вы в порядке с жертвой некоторого типобезопасности, вы можете сделать Recursive generi c:

interface Recursive<T extends Recursive> {
    List<T> getSubitems();

    default void processRecursive(Consumer<T> consumer) {
        consumer.accept((T) this); // sacrificing type-safety
        for (T item : getSubitems())
            consumer.accept(item);
    }
}

Теперь его можно использовать как и все хорошо с миром:

class Foo implements Recursive<Foo> {

Но кто-то может написать это, взломав код, потому что теперь this не является Bar, поэтому приведение будет вызывать исключение во время выполнения (возможно, зависит от Java версии, которую я считаю) :

class Foo implements Recursive<Bar> {
...