Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Function class addThen default method

I am learning Java-8 Lambda, I am trying to understand addThen default method in java.util.function.Function interface.As per my understanding addthen will first execute the First function and then it will execute the second method. So I created a program like below:

//Pojo class 
  class Bike {

    public Bike(String bikeName, int price, String bikeType) {
        this.bikeName = bikeName;
        this.price = price;
        this.bikeType = bikeType;
    }

    private String bikeType;
    private String bikeName;
    private int price;

    public String getBikeType() {
        return bikeType;
    }

    @Override
    public String toString() {
        return "Bike [bikeType=" + bikeType + ", bikeName=" + bikeName + ", price=" + price + "]";
    }

    public void setBikeType(String bikeType) {
        this.bikeType = bikeType;
    }

    public String getBikeName() {
        return bikeName;
    }

    public void setBikeName(String bikeName) {
        this.bikeName = bikeName;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

}

//Main class
public class FunctionInbuildDefaultMethodsExample {

    public static void main(String[] args) {
        learningAndThen();
    }

    static void learningAndThen() {

        Function<Bike, String> updateBikefunction = (Bike bike) -> {
            System.out.println("OldBike Name is::" + bike.getBikeName());
            bike.setBikeName("PULSOR-200CC");
            return bike.getBikeName();
        };

        Function<Bike, String> updateBikePriceFunction = (Bike bike) ->  {

            System.out.println("OldBike Price is::" + bike.getPrice());
            bike.setPrice(95000);
            return bike.getBikeName();

        };

        /*
         * First update Bike and then price
         * */
         /*Compilation error here*/
        Function<Bike,String> bikeFunction = updateBikefunction.andThen(updateBikePriceFunction);
        bikeFunction.apply( new Bike("PULSOR-125CC", 65000, "BAJAJ"));

    }
 }

I am getting an compilation error at the line

 Function<Bike,String> bikeFunction =
updateBikefunction.andThen(updateBikePriceFunction);

as

"The method andThen(Function) in the type Function is not applicable for the arguments (Function)"

, After looking into the source code of Function interface , I understand that addThen default method is looking for instance of type Function<String,Book>. My questions is, If addThen default method is supposed to execute the first function and then the next fuction which is passed as parameter , why the addThen default method of function interface is written in such a manner excepting instance of type Function<String,Book>.

like image 902
vicky Avatar asked Aug 23 '17 12:08

vicky


People also ask

Can a class override a default method?

One of the major reason for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions. If any class in the hierarchy has a method with same signature, then default methods become irrelevant. A default method cannot override a method from java.

What is default method in Java8?

Java 8 introduces default method so that List/Collection interface can have a default implementation of forEach method, and the class implementing these interfaces need not implement the same.

Can functional interface have default methods?

A functional interface can contain default and static methods which do have an implementation, in addition to the single unimplemented method.


4 Answers

andThen is applied to the result of that Function. So this would work for example:

Function<Bike, String> bikeFunction = updateBikefunction
                                       .andThen(s -> s.toUpperCase());

Since the result is a String from that Function.

like image 117
Eugene Avatar answered Oct 09 '22 20:10

Eugene


updateBikefunction = Bike -> String
updateBikePriceFunction = Bike -> String

updateBikefunction -> updateBikePriceFunction = Bike -> String -> Bike -> String
                                                        ^^^^^^^^^^^^^^

To form a chain of functions, the previous result should be an output for the next function. Here is this condition is broken.

The updateBikefunction could be changed to a Function<Bike, Bike>

    Function<Bike, Bike> updateBikefunction = (Bike bike) -> {
        ...
        return bike;
    };

to compile the line:

Function<Bike, String> bikeFunction = updateBikefunction.andThen(updateBikePriceFunction);
like image 20
Andrew Tobilko Avatar answered Oct 09 '22 21:10

Andrew Tobilko


The andThen method of Function is used as a pipe between functions.

Both your Functions are take in a Bike and return a String, that's the problem.

When you invoke [function1].andThen[function2] where both have the same parametrization of <Bike, String>, [function2] is expecting a Bike, but receiving a String from [function1].

See docs (my bold):

Returns a composed function that first applies this function to its input, and then applies the after function to the result. If evaluation of either function throws an exception, it is relayed to the caller of the composed function.

like image 44
Mena Avatar answered Oct 09 '22 20:10

Mena


Explanation

It's rather simple. A function does not output tuples like (Bike, String), it has an input Bike and an output String.

If you now compose functions, then the second function needs to use that output as its input like:

Function1: A -> B
Function2: B -> C
Composed:  A (-> B) -> C

Your functions are both

updateBikefunction:      Bike -> String
updateBikePriceFunction: Bike -> String

however the compiler expects the updateBikePriceFunction to use the output of updateBikefunction as its input, so it wants it to look like:

updateBikePriceFunction: String -> ...

You can also clearly see this in the documentation:

Interface Function<T,R>

andThen(Function<? super R,? extends V> after)

So the object on which you invoke the method has input T and output R. The function you need to use as argument has input ? super R and output ? extends V.


How to Fix

If you want to chain manipulation methods you should use Bike -> Bike and create an own method for such final results like getBikeNameFunction which then returns the name:

Function<Bike, Bike> updateBikefunction = (Bike bike) -> {
    System.out.println("OldBike Name is::" + bike.getBikeName());
    bike.setBikeName("PULSOR-200CC");
    return bike;
};

Function<Bike, Bike> updateBikePriceFunction = (Bike bike) ->  {
    System.out.println("OldBike Price is::" + bike.getPrice());
    bike.setPrice(95000);
    return bike;
};

Function<Bike, String> getBikeNameFunction = (Bike bike) -> {
    return bike.getBikeName();
};

You can now use it this way:

Function<Bike, String> bikeFunction = updateBikefunction
    .andThen(updateBikePriceFunction)
    .andThen(getBikeNameFunction);

Or alternatively just use the reference to the method like:

Function<Bike, String> bikeFunction = updateBikefunction
    .andThen(updateBikePriceFunction)
    .andThen(Bike::getName);
like image 45
Zabuzard Avatar answered Oct 09 '22 20:10

Zabuzard