Generics <? super> wildcard not working in java 1.8 with method reference or lambda

ashishjaintech :
    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();
        }

    }

Problem in (1) (2) & (3) is that, it should work as java 1.7 (1) : it should give error as only Document type should be accepted. (2) : it should give error as only super type of Document should be accepted. (3) : it should be working as super of RFP can hold Document object. Difference between <? super T> and <? extends T> in Java

Holger :

It’s weird to insist on “it should work as java 1.7”, when in Java 1.7 resp. Java 7 there were no method references at all.

When you write a statement like

Supplier<Document> s2 = RFP::new;

You have to make a distinction between the type of the variable, the type of the function, and the actual implementation. You can easily write the equivalent

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

due to covariant return types, you can also write

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

So the type of the variable matches the type of the function, whereas the implementation returns an instance of a more specific type.

When you write

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)

the type of the function will differ. You can not write new Supplier<? extends Document>() { … } nor new Supplier<? super Document>() { … } and so the compiler will infer a type that can be instantiated, which simply is the target type type without ? extends or ? super. So it’s equivalent to

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)

which are valid instantiations (like in the first block), followed by legal assignments.

The problem with

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

lies exactly in the type inference logic described above. The type inference will use the type without ? super, and (Supplier<RFP>)Document::new is not valid. So here, we have to provide an explicit type to make it valid:

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

which follows the same pattern as the second block.
You can not implement a Supplier<? super RFP>. You can implement a supplier like Supplier<Document> which is assignable to Supplier<? super RFP>. When the target type has wildcards, the strategy to just strip off the wildcards often helps but sometimes doesn’t.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=328083&siteId=1