Asif Mushtaq
Asif Mushtaq

Reputation: 3798

How generics really works as in parameters?

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

Answers (3)

Alexey Rykhalskiy
Alexey Rykhalskiy

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

Sweeper
Sweeper

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

algrid
algrid

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

Related Questions