We already talked about Java functional interfaces in the following article, which I recommend you read, before this article:
https://blog.sandbay.it/news/java/java-8-lambda-expressions/
Now I would like to talk to you about the various categories of functional interfaces most used and known.
Briefly repeating what has already been said in the other article, a functional interface is an interface with only one abstract method.
We can find many functional interfaces in java.util.function
package.
In “java.util.function” we have:
Suppliers
: its functional methodget()
never takes an argument and it always returns something.Consumers
: its functional methodaccept()
always takes an argument and never returns anything.Predicates
: (to test) its functional methodtest()
takes a value, does a logical test and returns tue or false (a boolean).Functions
: (the most generic) its functional methodapply()
takes an argument and returns a value (with a sub-category, theOperators
).
Using Suppliers – functional interfaces
The Supplier method returns ever something, even if you don’t need anything.
Supplier<Integer> answerSupplier = () -> 42;
Its declaration:
@FunctionalInterface
public interface Supplier<T> {
public T get();
}
There are more specific suppliers for autoboxing: IntSupplier
, DoubleSupplier
, LongSupplier
, to avoid autoboxing if you want a primitive.
An example is the new log()
method in Logger
that get in like arguments a status and a Supplier<String>
. With this overloaded method we can, for instance, do an heavy operation (an url connection) only when need that status log (the logger level is set on that level).
Logger logger = Logger.getLogger("Status log");
logger.setLevel(Level.SEVER); // log level
Supplier<String> status = () -> {
int timeout = 1000;
try (Socket socket = new Socket()) {
socket.connect(new IntSocketAddress("obyte.it", 80, timeout);
return "up";
} catch (IOException e) {
return "down";
}
};
try {
logger.log(Level.INFO, status); // only calls get() when level is INFO
} catch (Exception e) {
logger.log(Level.SEVERE, status);
}
Using Consumers
It’s the opposite of Supplier, it accepts an input parameter and never returns anything.
Its declaration:
@FunctionalInterface
public interface Consumer<T> {
public void accept(T t);
}
With an accept()
method that can take one argument, like an object, or a primitive with: IntConsumer
, DoubleConsumer
, LongConsumer
.
There also ObjIntConsumer
, ObjDoubleConsumer
, and ObjLongConsumer
for objects and primitives.
We have also BiConsumer
that get two arguments, userful for maps.
There is a forEach()
Java 8 method added to Iterable interface (an interface of lists) that takes a Consumer like argument.
List<String> potterNames = Arrays.asList("Harry", "James", "Lily");
potterNames.forEach(name -> System.out.println(name));
We know that we can’t modify variables from external of a lambda expression but we can cheat and modify only an attribute of those objects. This can be did.
Map<String, String> env = System.getenv();
User user = new User();
BiConsumer<String, String> findUsername = (key, value) -> {
if (key.equals("USER")) user.setUsername(value);
};
env.forEach(findUsername);
Another method that can be used with consumers is andThen()
, a default
method on Consumer.
Consumer<Potter> displayName = p -> System.out.println(p);
potterList.forEach(displayName.andThen(
p -> p.doPatronus();
));
We must declare first the consumer to attach to it the andThen()
method! We can’t do:
(p -> p).andThen(...); // not compiles!
And we can’t chain a Consumer
with a BiConsumer
and viceversa.
Using Predicates
It’s a generic functional interface that takes something and returns a boolean, testing it.
It functional method is test().
Its declaration:
@FunctionalInterface
public interface Predicate {
public boolean test();
}
One simply example:
Predicate<Potter> p = p -> p.getAge() >= 11;
Tests if a Potter is older or equals than 11 and can go to the Hogwarts school.
Potter harry = new Potter("Harry", 11);
p.test(harry); // true
A JDK method removeIf()
of ArrayList takes a Predicate.
There are some default methods: and()
, or()
, negate()
to chain predicates:
Predicate<Potter> name = p -> p.getName().equals("James");
Predicate<Potter> age = p -> p.getAge() == 11;
Using it:
Predicate<Potter> nameAndAge = p -> name.and(age).test(p);
// or, more concise:
Predicate<Potter> nameAndAge = name.and(age);
There are also DoublePredicate, IntPredicate, LongPredicate to avoid autoboxing and BiPredicate to take two arguments.
Using Functions
It’s a generic functional interface that takes an argument of a type and returns something of another type.
Its declaration:
@FunctionalInterface
public interface Function<T, R> {
public R apply(T t);
}
It has a functional method apply().
An example to change a numbe to string:
Function<Integer, String> horcruxNumber = h -> {
if (a == 7) return "seven horcruxes";
};
There is also a BiFunction that takes two arguments and returns one (type of) value.
BiFunction<String, String, String> nameSurname =
(name, surname) -> name + " " + surname;
In JDK we can find two examples of these functions: map.computerIfAbsent()
and map.replaceAll()
:
Map<String, String> map = new TreeMap<>();
// put on map...
// check if a key exists and, if not, run a lambda:
map.computeIfAbsent("aKey", k -> "a value"); // a very simple lambda
map.replaceAll((key, oldValue) -> oldValue.toUppeCase()); // BiFunction
The other default and static functions are: andThen()
(to chain Functions, like the Consumer’s andThen()), identity()
and compose()
.
compose()
is the same of andThen()
except it run functions in reverse order and identity()
returns only the input value. It’s a “do nothing” method.
Function<Integer, Integer> id = Function.identity();
id.apply(42); // returns 42
There are many other variations of Function: DoubleToIntFunction
, DoubleToLongFunction
, …, LongToIntFunction
, ToIntFunction
, ToIntBiFunction
, …and others.
They have a variation of first apply() method. For instance, IntToDoubleFunction
has applyAsDouble()
method.
Using Operators
The most famous Operator that we can know is the UnaryOperator. Operators extend Function but UnaryOperator has only a type.
Its declaration:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T>
An example of its use may be a mathematical operation:
UnaryOperator<Double log2 = v -> Math.log(v) / Math.log(2);
log2.apply(8.0);
Writing Java functional interfaces
Obviously we can create functional interfaces from us.
An example would be a Predicate
that accepts 3 arguments.
@FunctionalInterface
public interface TriPredicate<T, U, V> {
boolean test(T t, U u, V v);
}
And we would use it as we normally use any other functional interface:
(s, n, w) -> {
return s != null && s.equals(n) && s.equals(w);
}
That’s all on categories of Java functional interfaces.
Try it at home!
Way cool! Some extremely valid points! I appreciate you penning this post plus the rest of the site is also really good. Noni Agustin Sharma
Excellent article. Keep posting such kind of information on your page. Lelah Hewie Birch
I value the article. Really thank you! Really Cool. Jillie Alisander Bowie
I really like your article. Thanks for writing this. Averyl Merv Hachmann
Excellent post! We will be linking to this great post on our site. Keep up the great writing. Katharina Chucho Lynett
I really liked your article. Really thank you! Really Cool. Lorie Harp Ri
Howdy! I simply would like to give you a big thumbs up for the great info you have here on this post. I am coming back to your web site for more soon. Dulce Pren Desta
We are will rapidly as well as effectively generate a guarantee Premium remodelling manhattan. Marianna Josias Heshum