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
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.