Java Generics - difference in method declaration - java

What is the difference between the following two method declarations:
1. <R> Stream<R> myFunc(Function<? super T, ? extends R> mapper);
2. Stream<R> myFunc(Function<? super T, ? extends R> mapper);
For the 2nd declaration to compile, I need to add type parameter to class like this.
public class MyGenericsTest<T, R>
In this case compiler is ensuring that the return type of myFunc is determined at the compile time. The compiler could have known that from the method signature as well. I am confused on why these 2 declarations are treated differently by compiler.

By writing <R> Stream<R> myFunc(Function<? super T, ? extends R> mapper) you are telling the compiler that:
R is any class and is local to the method (by starting with<R> at the beginning)
The return type is a Stream of R
T is a class specified in the type parameter of MyGenericsTest<T> (if you dont specify it, it wont work as the compiler will not know T)
If you change to Stream<R> myFunc(Function<? super T, ? extends R> mapper), R and T are not local (no <R, T> at the beginning of the method) and the compiler expects them to be defined at a class level as MyGenericsTest<T, R>

The second form:
Stream<R> myFunc(Function<? super T, ? extends R> mapper);
really isn't any different from this:
Stream<String> myFunc(Function<? super T, ? extends String> mapper);
to the compiler, except that it uses a different type. The second one returns a stream of String. The first one returns a stream of R, whatever R is. The compiler already knows what String is. For the first one, the compiler has to know what R is, which means it has to be defined somewhere. It can be a generic parameter on an outer class, but it could also be a non-generic class that you import from somewhere else (written by someone who is very bad at coming up with meaningful names).
Keep in mind that although we often use single upper-case letters as generic parameters, that's just a human convention. The compiler just treats it like any other identifier.
But that's why your two examples are so different. The first one is a syntax that defines the method as a generic method with a type parameter that you're calling R. The second one is the exact same syntax as a method that returns Stream<String> or List<Integer> or something.

Related

Java Function interface with bounded wildcard

I'm trying to use java.util.Function object as an input to a library class.
Function<? extends MyEntity, List> mapper;
public MyLibraryClass(Function<? extends MyEntity,List> mapper) {
...
}
public void aMethod(ClassExtendsMyEntity e) {
mapper.apply(e);
}
The line mapper.apply(e) doesn't compile.
If I change to use ? super instead of ? extends, then the fail will be in the client using this library in the following code:
Function<ClassExtendsMyEntity, List> mapper = e -> e.getListObjects();
new MyLibraryClass(mapper);
Can anyone help explain why getting this issue and if there is a way to get this working?
Function<? extends MyEntity,List> mapper;
This does not say "any subclass of MyEntity can be passed to mapper". It says "there exists some subclass of MyEntity that can be passed to mapper". It might be ClassExtendsMyEntity, it might be MyEntity itself, or it might be some crazy class that you've never even heard of.
Function<T, S> provides one function: S apply(T arg). Since your argument type is ? extends MyEntity, the only valid argument you can pass to mapper.apply is a value which is of every subtype of MyEntity at once, and there is no value that satisfies that condition.
Put more technically, the first argument of Function<T, S> is, in principle, contravariant, so using the covariant ? extends T annotation makes little sense on it.
Depending on what you want, there are two solutions. If your function works for any MyEntity, then you should simply write Function<MyEntity, List> and forgo the generics/wildcards altogether. On the other hand, if you want MyLibraryClass to support only one subtype, but a subtype that you know about, then you need to make that a generic argument to MyLibraryClass.
public class MyLibraryClass<T extends MyEntity> {
Function<? super T, List> mapper;
public MyLibraryClass(Function<? super T, List> mapper) {
...
}
public void aMethod(T e) {
mapper.apply(e);
}
}
Again, the first type argument of Function is, in principle, contravariant, so we should use ? super T to bound it by our type argument T. That means that if I have a MyLibraryClass<ClassExtendsMyEntity>, the function inside that might be a Function<ClassExtendsMyEntity, List>, or a Function<MyEntity, List>, or a Function<Object, List>, since all of those support ClassExtendsMyEntity arguments. For more on the reasoning behind that, see What is PECS.
The declaration Function<? extends MyEntity,List> mapper means:
mapper expects an argument of some type X which is a subtype of MyEntity. The problem is, it doesn't tell what type it is.
And therefore the compiler can't verify if your call passing an instance of type ClassExtendsMyEntity is valid. After all X might be SomeOtherClassExtendingMyEntity.
In order to fix this just make the function type Function<MyEntity,List>

Signature of stream Map

Why does the following
<R> Stream<R> map(Function<? super T,? extends R> mapper)
not need to define T as well, as in
<R,T> Stream<R> map(Function<? super T,? extends R> mapper)
interface Stream<T>{ ... <R> Stream<R> map(Function<? super T, ? extends R> modifier); ... }
Because Streams iterate over elements of a given type, T is already known and is visible to all instance level methods (non-static methods in the class). The purpose of the mapping function is to return a Stream which intercepts elements in the base stream, and applies the function before continuing so all elements encountered will now be of type R.
Because the original stream iterates over elements of type T, passing a mapping function which expects inputs of T2 (if it were a method generic instead of class generic) is invalid. The function must expect inputs of type T (or a superclass of T). However, there are no restrictions needed on the output. As such, the function provided depends only on the type of the current stream.
<NEW_STREAM_TYPE> Stream<NEW_STREAM_TYPE> map(Function<? super CURRENT_STREAM_TYPE, ? extends NEW_STREAM_TYPE> modifier);

Question about Java generics and design of java.util.function.Function

question about Wildcard
Example:Student extends Person
Person person = new Person();
Student student = new Student();
List<? super Student> list = new ArrayList<>();
list.add(student); // success
list.add(person); // compile error
List<? extends Person> list2 = new ArrayList<>();
list2.add(person); // compile error
list2.add(student);// compile error
I have read the answer below a question "capture#1-of ? extends Object is not applicable"
You are using generic wildcard. You cannot perform add operation as class type is not determinate. You cannot add/put anything(except null) -- Aniket Thakur
Official doc:The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype
But why could list.add(student) compile successfully ?
Design of java.util.function.Function
public interface Function<T, R>{
//...
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
}
Why before is designed to Function<? super V, ? extends T> rather than Function<V,T> when the type of return is Function<V,R> and type of the input is V ? (It still can pass compile and use flexibly)
To understand these questions, you have to understand how generics work with subtyping (which is explicitly denoted in Java using the extends keyword). Andreas mentioned the PECS rules, which are their representations in Java.
First of all, I want to point out that the codes above can be corrected by a simple cast
ArrayList<? super Student> list = new ArrayList<>();
list.add(new Student());
ArrayList<Person> a = (ArrayList<Person>) list; // a covariance
a.add(new Person());
And compiles & runs well (rather than raising any exceptions)
The reason is simple, when we have a consumer (which takes some objects and consume them, such as the add method), we expect it to take objects of type no more than(superclasses) the type T we specified, because the process of consuming needs possibly any member(variables, methods etc.) of the type it wants, and we want to ensure that type T satisfy all the members the consumer requires.
On the contrary, a producer, which produces objects for us (like the get method), has to supply objects of type no less than the specified type T so that we can access any member that T has on the object produced.
These two are closely related to subtyping forms called covariance and contravariance
As for the second question, you can refer to the implementation of Consumer<T> as well (which is somewhat simpler):
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
the reason why we need this ? super T is that: when we are combining two Consumers using the method andThen, suppose that the former Consumer takes an object of type T, we expect the later to take a object of type no more than T so it would not try to access any member that T doesn't have.
Therefore, rather than simply writing Consumer<T> after but Consumer<? super T> after, we allow the former consumer (of type T) to be combined with a consumer that takes an object not exactly of type T, but maybe smaller then T, by the convenience of covariance. That makes the following codes sound:
Consumer<Student> stu = (student) -> {};
Consumer<Person> per = (person) -> {};
stu.andThen(per);
The compose method of type Function also applies, by the same consideration.
IMO This is probably the most complex concept in vanilla Java. So let's break this down a bit. I'll start with your second question.
Function<T, R> takes an instance t of type T and returns an instance r of type R. With inheritance that means that you could supply an instance foo of type Foo if Foo extends T and similarly return bar of type Bar if Bar extends R.
As a library maintainer who wants to write a flexible generic method, it's hard, and actually impossible, to know in advance all the classes which might be used with this method which extend T and R. So how are we going to write a method that handles them? Further, the fact that these instances have types which extend the base class is none of our concern.
This is where the wildcard comes in. During the method call we say that you can use any class which meets the envelope of the required class. For the method in question, we have two different wildcards using upper and lower bounded generic type parameters:
public interface Function<T, R>{
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
Lets now say that we want to take advantage of this method... for the example lets define some basic classes:
class Animal{}
class Dog extends Animal{}
class Fruit{}
class Apple extends Fruit{}
class Fish{}
class Tuna extends Fish{}
Imagine our function and transformation is defined as below:
Function<Animal, Apple> base = ...;
Function<Fish, Animal> transformation = ...;
We can combine these functions using compose to create a new function:
Function<Fish, Apple> composed = base.compose(transformation);
This is all fine and dandy, but now imagine that in the desired output function we actually only want to use Tuna as the input. If we did not use the lower-bounded ? super V as the input type parameter for the Function we pass to compose then we would get a compiler error:
default <V> Function<V, R> compose(Function<V, ? extends T> before)
...
Function<Tuna, Apple> composed = base.compose(transformation);
> Incompatible types:
> Found: Function<Fish, Apple>, required: Function<Tuna, Apple>
This happens because the return type for the call to compose specifies V as Tuna while transformation on the other hand specifies its "V" as Fish. So now when we try to pass transformation to compose the compiler requires transformation to accept a Tuna as its V and of course Tuna does not identically match Fish.
On the other hand, the original version of the code (? super V) allows us to treat V as a lower bound (i.e. it allows "contravariance" vs. "invariance" over V). Instead of encountering a mismatch between Tuna and Fish the compiler is able to successfully apply the lower bound check ? super V which evaluates to Fish super Tuna, which is true since Tuna extends Fish.
For the other case, imagine our call is defined as:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
If we did not have the wildcard ? extends T then we would get another error:
default <V> Function<V, R> compose(Function<? super V, T> before)
Function<Fish, Apple> composed = base.compose(transformation);
// error converting transformation from
// Function<Fish, Dog> to Function<Fish, Animal>
The wildcard ? extends T allows this to work as T is resolved to Animal and the wildcard resolves to Dog, which can satisfy the constraint Dog extends Animal.
For your first question; these bounds really only work in the context of a method call. During the course of the method, the wildcard will be resolved to an actual type, just as ? super V was resolved to Fish and ? extends T was resolved to Dog. Without the information from the generic signature, we would have no way for the compiler to know what class can be used on the type's methods, and therefore none are allowed.

Eclipse's "extract to method" on Function results in compilation error

Starting with a class A with 2 fields, name and id, a constructor and getters, I wrote this test, which runs green:
#Test
public void test() {
List<A> list = Arrays.asList(new A(null, "a"), new A(null, "b"));
Collections.sort(list,
Comparator
.comparing(A::getName, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(A::getId, Comparator.nullsLast(Comparator.reverseOrder())));
assertThat(list.get(0).id, is("b"));
}
However, if I select Eclipse's quick fix "extract to method" on A::getName:
I suddenly get 2 compilation errors on the next line (thenComparing(...)):
#Test
public void test() {
List<A> list = Arrays.asList(new A(null, "a"), new A(null, "b"));
Collections.sort(list,
Comparator
.comparing(extracted(), Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(A::getId, Comparator.nullsLast(Comparator.reverseOrder())));
assertThat(list.get(0).id, is("b"));
}
private Function<? super A, ? extends String> extracted() {
return A::getName;
}
Saying:
The type Test.A does not define getId(capture#1-of ? super Test.A) that is applicable here
and
The method thenComparing(Function<? super capture#1-of ? super Test.A,? extends U>, Comparator<? super U>) in the type Comparator is not applicable for the arguments (A::getId, Comparator<Comparable<? super Comparable<? super T>>>)
Why does this result in an error? What am I doing wrong?
It's the refactoring that's to blame. It creates two new wildcards that need to be captured before / during type inference. After this, types can no longer be unified and inference cannot find a solution.
Another bug is that javac 8 accepted this code, but this has been fixed in javac 9 (I tried build 9-ea+118).
In that version javac's error message reads:
error: no suitable method found for thenComparing(A::getId,Comparator<T#1>)
.thenComparing(A::getId, Comparator.nullsLast(Comparator.reverseOrder())));
^
method Comparator.<U#1>thenComparing(Function<? super CAP#1,? extends U#1>,Comparator<? super U#1>) is not applicable
(cannot infer type-variable(s) U#1
(argument mismatch; invalid method reference
method getId in class A cannot be applied to given types
required: no arguments
found: CAP#1
reason: actual and formal argument lists differ in length))
method Comparator.<U#2>thenComparing(Function<? super CAP#1,? extends U#2>) is not applicable
(cannot infer type-variable(s) U#2
(actual and formal argument lists differ in length))
where T#1,T#2,U#1,T#3,U#2 are type-variables:
T#1 extends T#2
T#2 extends Comparable<? super T#2>
U#1 extends Object declared in method <U#1>thenComparing(Function<? super T#3,? extends U#1>,Comparator<? super U#1>)
T#3 extends Object declared in interface Comparator
U#2 extends Comparable<? super U#2> declared in method <U#2>thenComparing(Function<? super T#3,? extends U#2>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: A from capture of ? super A
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
EDIT:
The bug in javac was most likely https://bugs.openjdk.java.net/browse/JDK-8039214 which was indeed resolved for version 9. This is said to be the "master bug" for a cluster of related bugs dealing with javac's failure to correctly handle wildcard captures. In that bug the following text is proposed for the release notes:
The javac compiler's behavior when handling wildcards and "capture" type variables has been improved for conformance to the language specification. This improves type checking behavior in certain unusual circumstances. It is also a source-incompatible change: certain uses of wildcards that have compiled in the past may fail to compile because of a program's reliance on the javac bug.
I believe in both cases (javac 9 and ecj) the complaint regarding getId is secondary, result of inference having failed already.
At a closer look, the error against getId is the primary cause for failure to type-check:
The resolved type of comparing(..) is Comparator<capture#1-of ? super A> (capture induced by capturing the resolved type of extracted()).
The target type for the method reference A::getName is Function<? super capture#1-of ? super A,? extends U> (from the first parameter of <U> Comparator<T> thenComparing(Function<? super T, ? extends U>, Comparator<? super U>)
The non-wildcard parameterization (JLS 9.9) of this target type is <capture#1-of ? super A, U>.
Hence the method reference must implement the function type U apply(capture#1-of ? super A)
To use the argument of apply as the receiver for getId we need a value of type A, but we only have guarantees that the provided value is a (unknown) super type of A.
javac reverts to expecting an argument of the given type, while getId takes no argument -> hence the 1st message regarding argument list lengths.
Ergo getId does not implement the expected functional type.
This vaguely matches the description in JDK-8039214 that javac 8 erroneously uses a bound of the capture, instead of the capture itself. I say vaguely, because the bug speaks of upper bounds, while here javac 8 seems to use a lower bound even.

How do I defined a method bounded type on another bounded type?

I want to define a method that defines its types like this:
List<R> toList(JsArray<T> array)
such that T is bounded such that it is both:
T extends SomeClass, and
T extends R
I've tried things like this, to no avail:
<R, T extends R & SomeClass> List<R> toList(JsArray<T> array)
(For the curious, this is to be able to use GWT Overlay Types with Interfaces)
How about this?
<T extends SomeClass> List<? super T> toList(JsArray<T> array)
EDIT:
I think I see your use case. From comments, you want to specify the return type so you don't have an unchecked cast.
But you don't actually pass anything in to tell the method what to create. Since I could always call toList() with R = T, the only thing you could do that always works (for all R super T) is to return a List<T>, which makes the R parameter unnecessary... but that's not what you want.
You have to pass something in to tell the method what kind of object to instantiate. Usually we could to that like:
<R, T extends SomeClass> List<R> toList(JsArray<T> array, Class<R> cls)
and call it like
toList(arrayIHave, WhatIWant.class)
Now this will work, but you will complain that it has error conditions, because the "T extends R" constraint isn't captured. But really, even if you add the constraint in, you'll still have error conditions.
I can use an arbitrary interface for R by making a T that extends SomeClass and implements R. There is no way you can meaningfully instantiate an arbitrary interface, so for many R,T pairs that satisfy your constraints, you will have to return null or throw an exception or something.
No doubt there is some rule that dictates which R you will return, but there is no way to even think about specifying generic constraints that capture that rule, so there will be an unchecked conversion somewhere. If you use a signature like this, at least you can put it inside the method instead of having warnings or annotations everywhere you use it.
I fear this does not work.
The correct syntax would be
<R, T extends R & SomeClass> List<R> toList(JsArray<T> array)
But this will give you the following error message
Cannot specify any additional bound SomeClass when first bound is a type parameter
I do not see how you could make it work to enforce both constraints.

Categories

Resources