Reputation: 3798
I'm little confused about how the generics works? I'm learning about function
API in java and there I just test Function
interface and got confused about compose
method that how the generics is working in compose
method.
Reading the generics on the java official tutorial website I realize that if we have any generic type in the method return or parameters we have to declare that type in the signature of method as explained below.
Here is the method I read in official docs tutorial.
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
Above method have two types, K, V which are declared in the signature after the static keyword as but when I read java Function
API there is one method called compose
and the signature of the compose is as
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
1) The first question where is the T & R declared? which are being used in the return type and in the parameter. Or my understanding is wrong?
Then I read more in generics tutorials and then I try to understand the concept of super
and extends
in generics and read here then I test compose
method more and then confused again about how the super
and extends
works in the compose
method?
public static void main(String... args){
Function<Integer, String> one = (i) -> i.toString();
Function<String, Integer> two = (i) -> Integer.parseInt(i);
one.compose(two);
}
As above I have declared two Function with lamdas. One is having Integer input and String output the other one is reversed from it.
2) The second question is that how Integer
and String
are related to extends
and super
? There is no relation between String
and Integer
class no one is extending each other then how it is working?
I tried my best to explain my question/problem. Let me know what you didn't understand I will try again.
Upvotes: 0
Views: 103
Reputation: 418
I will try to explain from zero;
interface Function<T, R>
- this is interface with one method, which must be implemented R apply (T)
;
in Java prior to 8 we must write:
Function<Integer, String> one = new Function<Integer, String>() {
@Override
public String apply(Integer i) {
return i.toString();
}
};
now you can use it:
String resultApply = one.apply(5);
now, I think, you get the idea.
Upvotes: 0
Reputation: 271725
Where are T
and R
defined?
Remember, compose
is declared in the Function
interface. It can not only use generic parameters of its own, but also the type's generic parameters. R
and T
are declared in the interface declaration:
interface Function<T, R> {
...
}
What are ? extends
and ? super
?
?
is wildcard. It means that the generic parameter can be anything. extends
and super
give constraints to the wildcard. ? super V
means that whatever ?
is, it must be a superclass of V
or V
itself. ? extends T
means that whatever ?
is, it must be a subclass of T
or T
itself.
Now let's look at this:
Function<Integer, String> one = (i) -> i.toString();
Function<String, Integer> two = (i) -> Integer.parseInt(i);
one.compose(two);
From this, we can deduce that T
is Integer
and R
is String
. What is V
? V
must be some type such that the constraints Function<? super V, ? extends T>
is satisfied.
We can do this by substituting the argument we passed in - Function<String, Integer>
- to get String super V
and Integer extends Integer
.
The second constraint is satisfied already while the first constraint now says that String
must be a super class of V
or String
itself. String
cannot have subclasses so V
must be String
.
Hence, you can write something like:
Function<String, String> f = one.compose(two);
but not
Function<Integer, String> f = one.compose(two);
When you compose a Function<Integer, String>
and a Function<String, Integer>
you cannot possibly get a Function<Integer, String>
. If you try to do this, V
is automatically inferred to be Integer
. But String super Integer
is not satisfied, so the compilation fails. See the use of the constraints now? It is to avoid programmers writing things that don't make sense. Another use of the constraints is to allow you to do something like this:
Function<A, B> one = ...
Function<C, SubclassOfB> two = ...
Function<SubclassOfC, B> f = one.compose(two);
There is no relationship between Integer
and String
in this case, it's all about V
.
Upvotes: 1
Reputation: 5954
1) The compose
function is part of Interface Function<T,R>
. As you can see in documentation for this interface:
Type Parameters:
T - the type of the input to the function
R - the type of the result of the function
2) The super
and extends
constraints in questions aren't applied to T & R, they're applied to the generic type parameters of a function that you pass in as an argument to the compose
function.
Basically this means that if you have:
Function<ClassA, ClassB> one;
Function<SomeSuperClassOfC, SomeSubclassOfA> two;
then it's valid to call
Function<ClassC, ClassB> three = one.compose(two)
Upvotes: 1