Ron Santis
Ron Santis

Reputation: 45

Confusion in functioning of Lambda expression

I am trying to learn lambda expressions. I am currently referring to OCP study guide.

In one example of lambda expression there is a Animal class and another another interface called CheckTrait. The functioning of lambda expression is very confusing for me. Example code is as shown below:

Without lambda expression.

public interface CheckTrait {
    public boolean test(Animal a);
}


public class Animal  implements CheckTrait{

    private String species;
    private boolean canHop;
    private boolean canSwim;

    public Animal(String species, boolean canHop, boolean canSwim) {
        super();
        this.species = species;
        this.canHop = canHop;
        this.canSwim = canSwim;
    }

    public boolean canHop() {
        return this.canHop;
    }

    public String toString() {
        return species;
    }

    public boolean canSwim() {
        return this.canSwim;
    }

    @Override
    public boolean test(Animal a) {

        return a.canHop();
    }
}

    public class FindMathcingAnimals {

    private static void print(Animal animal, CheckTrait trait) {
        if(trait.test(animal)){
            System.out.println(animal+" can hop" );
        }else {
            System.out.println(animal+" cannot hop");
        }

    }

    public static void main(String[] args) {

        Animal animal2= new Animal("Kangaroo", true, false);    
        print(animal2,animal2);
    }

}

When we run the above class we get the output as Kangaroo can hop

With lambda expression

public interface CheckTrait {
    public boolean test(Animal a);
}


public class Animal  {

    private String species;
    private boolean canHop;
    private boolean canSwim;

    public Animal(String species, boolean canHop, boolean canSwim) {
        super();
        this.species = species;
        this.canHop = canHop;
        this.canSwim = canSwim;
    }

    public boolean canHop() {
        return this.canHop;
    }

    public String toString() {
        return species;
    }

    public boolean canSwim() {
        return this.canSwim;
    }


}


public class FindMathcingAnimals {

    private static void print(Animal animal, CheckTrait trait) {
        if(trait.test(animal)){
            System.out.println(animal+" can hop" );
        }else {
            System.out.println(animal+" cannot hop");
        }

    }

    public static void main(String[] args) {

        Animal animal2= new Animal("Kangaroo", true, false);    
        print(animal2,s->s.canHop());
    }

}

My Question is, In the below line print(animal2,s->s.canHop());

I am not passing any value to s but just an expression, then how is the program computing the result? I am aware that it is taking the animal object and checking the canhop variable just by intuition,but I am not understanding the logic here. Kindly provide your thoughts.

Edit: based on Aominè's answer:

So, can i conclude that. In the print method I am passing the behavior for CheckTrait, and in the print method can be reduced as:

step1: if(trait.test(animal)){

step2: if(s.canHop())

step3: if(animal.canHop)

the above steps are just for my understanding.

Upvotes: 2

Views: 669

Answers (3)

Ousmane D.
Ousmane D.

Reputation: 56393

I am not passing any value to s but just an expression, then how is the program computing the result?

below you pass in a behaviour (a function) as the second argument to the print method which will be used within the print method.

print(animal2, s -> s.canHop());

this function reads as "given an element say s, call the canHop() method on the element"

now within the print method when you do:

if(trait.test(animal)){ ... }

you're calling trait.test(animal) and passing the animal object to the function we passed in, we then apply the function to the input animal.

if it helps this:

print(animal2,s -> s.canHop());

can be seen as being the same as:

print(animal2, new CheckTrait() {
       @Override
     public boolean test(Animal s) {
           return s.canHop();
     }
});

edit:

as you've mentioned the method calls can be seen as:

if(trait.test(animal)){..} -> if(s.canHop()){..} -> if(animal.canHop){..} // pseudocode

Upvotes: 3

Leo Aso
Leo Aso

Reputation: 12463

I assume you are not familiar with functional programming so you can look at it this way - a lambda is like an abbreviation. Basically, CheckTrait is a class that contains a method that takes an Animal and returns a boolean indicating whether it has a cerain property. It doesn't perform any external operations or change any values or anything, it just computes one value (boolean) from an other (Animal). This is a function, in the purest sense.

In older versions of Java, to create an object that represents a CheckTrait function, you would have to do this

CheckTrait canHop = new CheckTrait() {
    public boolean test(Animal a) {
        return a.canHop();
    }
}
Animal a = ...;
canHop.test(a);

This is a lot to type, especially since the only important details here are the input argument (Animal a), and the output function (return a.canHop()). So Java borrowed a syntax from functional programming languages where you can specify just these two elements, leaving out all he noise, and it automatically translates to the code above.

CheckTrait canHop = (Animal a) -> { return a.canHop(); }
Animal a = ...;
canHop.test(a);

This only works for interfaces with a single method though. Even better they made it so if the compiler can guess the input type, and the function consists only of a return statement, you can omit the type and even the return keyword.

CheckTrait canHop = a -> a.canHop();
Animal a = ...;
canHop.test(a);

And that's how lambda's work. They are basically shortened forms of anonymous classes.

TLDR; print(animal2,s->s.canHop()) is short for

print(animal2, new CheckTrait() {
    public boolean test(Animal a) {
        return a.canHop();
    }
});

Upvotes: 1

Kraylog
Kraylog

Reputation: 7553

A lambda expression is just a function.

The form

s -> s.canHop() 

specifies a function that receives a single parameter, and invokes the canHop function on that parameter.

Since print is expecting a CheckTrait, the compiler knows that the type of the parameter is Animal.

Upvotes: 0

Related Questions