ДженерикиПодстановочный знак не работает в Java 1.8 со ссылкой на метод или лямбда - PullRequest
1 голос
/ 19 сентября 2019
    package com.test.lambda;

    import java.util.function.Supplier;

    class Document {
        void printAuthor() {
            System.out.println("Document-Author");
        }
    }

    class RFP extends Document {
        @Override
        void printAuthor() {
            System.out.println("RFP-Author");
        }
    }

    public class TestLambda1 {
        public static void function21() {
            Supplier<Document> s1 = Document::new; // working
            Supplier<Document> s2 = RFP::new; // (1)

            Supplier<? extends Document> s3 = Document::new; // working
            Supplier<? extends Document> s4 = RFP::new; // working

            Supplier<? super Document> s5 = Document::new; // working
            Supplier<? super Document> s6 = RFP::new; // (2)

            Supplier<? super RFP> s7 = Document::new; // (3)
            Supplier<? super RFP> s8 = RFP::new; // working

        }

        public static void main(String[] args) throws Exception {
            function21();
        }

    }

Проблема в (1) (2) и (3) заключается в том, что он должен работать как Java 1.7 (1): он должен выдавать ошибку, так как должен быть принят только тип документа.(2): он должен давать ошибку, так как должен быть принят только супер тип документа.(3): он должен работать, так как супер RFP может содержать объект Document. Разница междуа такжена Java

1 Ответ

3 голосов
/ 19 сентября 2019

Странно настаивать на том, что «это должно работать как Java 1.7», когда в Java 1.7 соотв.В Java 7 вообще не было ссылок на методы.

Когда вы пишете оператор типа

Supplier<Document> s2 = RFP::new;

Вы должны различать тип переменной и тип функции,и фактическая реализация.Вы можете легко написать эквивалент

Supplier<Document> s2 = new Supplier<Document>() {
    public Document get() {
        return new RFP();
    }
}; // (1)

благодаря ковариантным типам возврата, вы также можете написать

Supplier<Document> s2 = new Supplier<Document>() {
    public RFP get() {
        return new RFP();
    }
}; // (1)

Таким образом, тип переменной соответствует типу функции, тогда как реализациявозвращает экземпляр более определенного типа.

Когда вы пишете

Supplier<? extends Document> s3 = Document::new; // working
Supplier<? extends Document> s4 = RFP::new; // working

Supplier<? super Document> s5 = Document::new; // working
Supplier<? super Document> s6 = RFP::new; // (2)

, тип функции будет отличаться.Вы не можете писать new Supplier<? extends Document>() { … } или new Supplier<? super Document>() { … }, и поэтому компилятор выведет тип, который может быть создан, который просто является типом целевого типа без ? extends или ? super.Так что это эквивалентно

Supplier<? extends Document> s3 = (Supplier<Document>)Document::new; // working
Supplier<? extends Document> s4 = (Supplier<Document>)RFP::new; // working

Supplier<? super Document> s5 = (Supplier<Document>)Document::new; // working
Supplier<? super Document> s6 = (Supplier<Document>)RFP::new; // (2) just like (1)

, которые являются действительными экземплярами (как в первом блоке), за которыми следуют юридические присвоения.

Проблема с

Supplier<? super RFP> s7 = Document::new; // (3)

лежит точнов логике вывода типа, описанной выше.Вывод типа будет использовать тип без ? super, а (Supplier<RFP>)Document::new недопустим.Поэтому здесь мы должны предоставить явный тип, чтобы сделать его действительным:

Supplier<? super RFP> s7 = (Supplier<Document>)Document::new; // (3)

, который следует той же схеме, что и второй блок.
Вы не можете реализовать Supplier<? super RFP>.Вы можете реализовать такого поставщика, как Supplier<Document>, который может быть назначен на Supplier<? super RFP>.Когда у целевого типа есть символы подстановки, стратегия простого удаления символов подстановки часто помогает, но иногда нет.

...