2

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.

1
  • 5
    You can read s -> s.canHop() as (Animal s) -> { return (s.canHop); }. Java is able to infer the type of s since print(...) expects an Animal and a CheckTrait, which maps an Animal on a boolean, as parameters. Commented Dec 23, 2017 at 12:39

3 Answers 3

3

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
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the detailed explanation.
please let me know, If Its correct to have the above mentioned assumptions (shown in edits below the question), just for my easier understanding.
1

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();
    }
});

1 Comment

Note that in this case you can use a method reference: print(animal2, s::canHop).
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.