Site Search:

Write a simple Lambda expression that consumes a Lambda Predicate expression


Back OCAJP



One of the things human being does best is predicate. When we come across a thing or situation, we evaluate it before taking action. Java created an interface for this:

public interface Predicate<T> {
  boolean test(T t);
}

A class implementing this interface can test object with class type T, test returns an answer true or false.


OCAJP>cat test.java 
import java.util.*;
import java.util.function.*;
class test {
  public static void main(String...args) {
    test t = new test();
    List<Mushroom> mushrooms = new ArrayList<>();
    mushrooms.add(new Mushroom("Shiitake", false));
    mushrooms.add(new Mushroom("Chanterelles", false));
    mushrooms.add(new Mushroom("Amanitas", true));
    t.eat(mushrooms, new MushroomChecker());
  }
  void eat(List<Mushroom> collections, Predicate<Mushroom> die) {
    for(Mushroom m : collections) {
      if(!die.test(m)) {
        System.out.println(m.name + " is edible");
      } else {
        System.out.println("my children, swear to my dead body, never eat " + m.name);
      }
    }
  }
}

class Mushroom {
  String name = "fungi"; 
  boolean poisonous = true;
  public Mushroom(String name, boolean poisonous) {
    this.name = name;
    this.poisonous = poisonous;
  }
}

class MushroomChecker implements Predicate<Mushroom> {
  public boolean test(Mushroom m) {
    return m.poisonous;
  }
}
OCAJP>javac test.java 
OCAJP>java test
Shiitake is edible
Chanterelles is edible
my children, swear to my dead body, never eat Amanitas


Here is a problem, there is more than one way to test edible mushroom, MushroomChecker is just one of them. For example, we can lookup edible mushroom book.
t.eat(mushrooms, new MushroomBookishChecker());

class MushroomBookishChecker implements Predicate<Mushroom> {
  public boolean test(Mushroom m) {
    return !m.name.equals("Shiitake") && !m.name.equals("Chanterelles");
  }
}

for this simple java example, we can fake another one.
t.eat(mushrooms, new MushroomDareDevilChecker());

class MushroomDareDevilChecker implements Predicate<Mushroom> {
  public boolean test(Mushroom m) {
    return !m.name.startsWith("S") && !m.name.startsWith("C");
  }
}

There could have thousands of strategy of testing edible Mushroom in human knowledge sphere, for example we can lookup poisonous mushroom book, here we only conjure up 3 of them in this example, but you should have gotten the idea.



The method
void eat(List<Mushroom> collections, Predicate<Mushroom> die) 
its second parameter expects a a class that provides a function that tests class Mushroom. 
What we need to do is creating a new class that provides function test that tests Mushroom, then pass that class's object as method parameter into method eat, there is lots of typing. Lambda just abstract these requirement into a function variable without any waste of typing to create a new class and initialize its objects.

MushroomChecker now shrinks into lambda
(Mushroom m) -> {return m.poisonous;}
It can be passed into eat as second parameter
t.eat(mushrooms, (Mushroom m) -> {return m.poisonous;});

MushroomBookishChecker shrinks into
(Mushroom m) -> {return !m.name.equals("Shiitake") && !m.name.equals("Chanterelles");}

 MushroomDareDevilChecker shrinks into
(Mushroom m) -> {return !m.name.startsWith("S") && !m.name.startsWith("C");}

The syntax can even lazier,  (Mushroom m) -> {return m.poisonous;} have the following equivalent:
m -> {return m.poisonous;}
m -> m.poisonous

So let's summarize the syntax for OCAJP test's sake.

At left side of -> the valid syntax have:
(Mushroom m)
m
(m)

At right side of -> the valid syntax have:
{return m.poisonous;}
m.poisonous

The following syntax are also valid:
caller(()->true); //no parameter
caller(a->a.append("o"));
caller((StringBuilder s)->{return s.append("o");});
caller((s,p)->s.append("o"));  //2 parameters
caller((s,p)->{int q=0; return s.append("o");});  //2 parameters, initialize another irrelevant parameter
caller((String s, String p)->s + b); //2 parameters, both specify parameter type


OCAJP>cat test.java 
import java.util.*;
import java.util.function.*;
class test {
  public static void main(String...args) {
    test t = new test();
    List<Mushroom> mushrooms = new ArrayList<>();
    mushrooms.add(new Mushroom("Shiitake", false));
    mushrooms.add(new Mushroom("Chanterelles", false));
    mushrooms.add(new Mushroom("Amanitas", true));
    System.out.println(" MushroomChecker equivalent Lambda ");
    t.eat(mushrooms, (Mushroom m)-> {return m.poisonous;});
    System.out.println(" MushroomBookishChecker equivalent Lambda ");
    t.eat(mushrooms, m -> {return !m.name.equals("Shiitake") && !m.name.equals("Chanterelles");});
    System.out.println(" MushroomDareDevilChecker equivalent Lambda ");
    t.eat(mushrooms, m -> !m.name.startsWith("S") && !m.name.startsWith("C"));
  }
  void eat(List<Mushroom> collections, Predicate<Mushroom> die) {
    for(Mushroom m : collections) {
      if(!die.test(m)) {
        System.out.println(m.name + " is edible");
      } else {
        System.out.println("my children, swear to my dead body, never eat " + m.name);
      }
    }
  }
}

class Mushroom {
  String name = "fungi"; 
  boolean poisonous = true;
  public Mushroom(String name, boolean poisonous) {
    this.name = name;
    this.poisonous = poisonous;
  }
}
OCAJP>javac test.java 
OCAJP>java test
 MushroomChecker equivalent Lambda 
Shiitake is edible
Chanterelles is edible
my children, swear to my dead body, never eat Amanitas
 MushroomBookishChecker equivalent Lambda 
Shiitake is edible
Chanterelles is edible
my children, swear to my dead body, never eat Amanitas
 MushroomDareDevilChecker equivalent Lambda 
Shiitake is edible
Chanterelles is edible
my children, swear to my dead body, never eat Amanitas


Last but not least, OCAJP test ArrayList.removeIf() method, which takes a predicate as parameter.


OCAJP>cat test.java 
import java.util.*;
class test {
  public static void main(String...args) {
    test t = new test();
    t.tester();
  }
  public void tester() {
    List<Long> numbers = new ArrayList<>(); 
    numbers.add(1L);
    numbers.add(2L);
    numbers.add(3L);
    numbers.add(6L);
    numbers.add(7L);
    System.out.println(numbers);
    numbers.removeIf(s->s.intValue()<5);
    System.out.println(numbers);
  }
}
OCAJP>javac test.java 
OCAJP>java test
[1, 2, 3, 6, 7]
[6, 7]