5

I was reading a not related thread , when I read a comment: Any time I find myself needing a multi-line lambda, I move the lines to a private method and pass the method reference instead of the lambda.

I was asking: which is the correct way to implement this behaviour? With a boolean method as posted in comment, or with predicate?


Example: let's say I wanna check if a Table is usable, where usable means isClean, isEmpty, hasChair.

class Table{
    public boolean hasChair(){...}
    public boolean isClean(){...}
    public boolean isEmpty(){...}
}

I can implement my filtering test for my list List<Table> tablesList = Arrays.asList(table1,table2,table3,table4); in 2 ways: the first with boolean:

public boolean isUsable(){
    return hasChair() && isClean() && isEmpty();
}

And use it with tablesList.stream().filter(Table::isUsable)

The second way is with predicate:

public Predicate<Table> isUsable(){
    return table -> table.isEmpty() && table.isClean() && table.hasChair();
}

Usable with tablesList.stream().filter(isUsable())


Which is the correct implementation? Why choose one instead of other? Is there any big difference?

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Leviand
  • 2,745
  • 4
  • 29
  • 43
  • 3
    Both are correct and while each has pros and cons, they are not even contradicting. You can add both, when you don’t use the same name (I would change the predicate returning method’s name). The predicate returning method could even built on the other, e.g. `public boolean isUsable() { return hasChair() && isClean() && isEmpty(); } public static Predicate usable() { return Table::isUsable; }`
    – Holger Oct 22 '18 at 10:44

3 Answers3

6

I think you meant for the second example

public static Predicate<Table> isUsable(){
    return table -> table.isEmpty() && table.isClean() && table.hasChair();
}

Which might already suggest this form is likely to confuse the reader. Without static you could write table.isUsable() or Table::isUsable but it wouldn't do what you think.

Which is the correct implementation?

I would prefer the Table::isUsable as it can also be used as table.isUsable for an instance.

Why choose one instead of other?

I feel the first example is more natural and less confusing.

The second form is more useful for manipulating Predicates e.g. Predicate.or(Predicate)

Is there any big difference?

In this case, using a Stream is likely to be slower, but more importantly, more likely to confuse.

One advantage of the method returning a Predicate is it could be added to any class e.g. you can't alter Table for some reason.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
5

Having Predicate<Table> isUsable() assumes that you would always require that logic to be used in places that require a Predicate<Table> instance, which is a limitation.

On the other hand, having boolean isUsable() gives you the flexibility of using Table::isUsable where a Predicate<Table> is required, or using Table::isUsable as implementation of some other functional interfaces (that matches the signature of that method) or directly calling t.isUsable() for a specific Table instance. Hence I find this alternative more useful.

Eran
  • 387,369
  • 54
  • 702
  • 768
-1
static List<Predicate<Table>> predicateList = Arrays.asList(Table::hasChair, Table::isClean);

static boolean isUsable(Table table) {
    return predicateList.stream().allMatch(p -> p.test(table));
}

use isUsable:

List<Table> tablesList = ...
Stream<Table> usableTables = tablesList.stream().filter(Table::isUsable);
Ilya
  • 720
  • 6
  • 14