I have been experimenting with Functional Programming in Java for a while now, and noticed that I started to prefer the use of the @FunctionalInterface
functions from the java.util.function
package such as Functions, BiFunctions, UnaryOperators, Predicates, BiPredicates, etc. instead of simple private methods in my classes. I am aware that their application is much more recommend as to be passed as arguments to another function, and that's how I usually tend to use them, but I now just find them immediate and somehow better.
I in fact now tend to declare some of these as variables to be then used in my classes when needed.
I don't seem to find anywhere guidelines or cons on the use of these instead of simple methods.
So: are there downsides using them this way?
- An example:
Why prefer:
private boolean foo(final int a, final int b){
return a < b;
}
instead of:
private final BiPredicate<Integer,Integer> foo = (a,b) -> a < b;
an example of how I tend to use them from my latest project:
private final BiFunction<BoardPosition, Pair<Integer, Integer>, BoardPosition> sumBoardPosWithPair = (pos,
pair) -> new BoardPositionImpl(pos.getX() + pair.getX(), pos.getY() + pair.getY());
private final Function<Pair<Integer, Integer>, UnaryOperator<BoardPosition>> unaryCreator = (
axis) -> (p) -> this.sumBoardPosWithPair.apply(p, axis);
/**
* If you need to call the fromFunction method twice for specular directions use
* this TriFunction specularNoLimitDirection instead.
*/
private final TriFunction<Piece, Vectors, Board, Set<BoardPosition>> specularNoLimitDirection = (piece, axis,
board) -> Stream.concat(
this.fromFunction(this.unaryCreator.apply(axis.getAxis()), piece, board,
board.getColumns() + board.getRows()).stream(),
this.fromFunction(this.unaryCreator.apply(axis.getOpposite()), piece, board,
board.getColumns() + board.getRows()).stream())
.collect(Collectors.toSet());
protected final Set<BoardPosition> fromFunction(final UnaryOperator<BoardPosition> function, final Piece piece,
final Board board, final int limit) {
/*
* The "function.apply" at the seed of the Stream.Iterate is used to skip the
* first element, that's itself, in fact a piece can't have as a possible move
* it's original position.
*/
final List<BoardPosition> positions = Stream.iterate(function.apply(piece.getPiecePosition()), function)
.takeWhile(board::contains)
.takeWhile(x -> board.getPieceAtPosition(x).isEmpty()
|| !board.getPieceAtPosition(x).get().getPlayer().equals(piece.getPlayer()))
.limit(limit).collect(Collectors.toList());
final Optional<BoardPosition> pos = positions.stream().filter(i -> board.getPieceAtPosition(i).isPresent()
&& !board.getPieceAtPosition(i).get().getPlayer().equals(piece.getPlayer())).findFirst();
/*
* The sublist excludes the last n-th element of the high-endpoint, for this
* reason we need to add 1.
*/
return pos.isEmpty() ? new HashSet<>(positions)
: new HashSet<>(positions.subList(0, positions.indexOf(pos.get()) + SINGLE_INCREMENT));
}