I've come across many questions in regards of Java8 in-built Functional Interfaces, including this, this, this and this. But all ask about "why only one method?" or "why do I get a compilation error if I do X with my functional interface" and alike. My question is: what is the existential purpose of these new Functional Interfaces, when I can use lambdas anyway in my own interfaces?
Consider the following example code from oracle documentation:
// Approach 6: print using a predicate
public static void printPersonsWithPredicate(List<Person> roster,
Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
System.out.println(p);
}
}
}
OK, great, but this is achievable with their own example just above (an interface with a single method is nothing new):
// Approach 5:
public static void printPersons(<Person> roster,
CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
System.out.println(p);
}
}
}
interface CheckPerson {
boolean test(Person p);
}
I can pass a lambda to both methods.
1st approach saves me one custom interface. Is this it?
Or are these standard functional interfaces (Consumer, Supplier, Predicate, Function) are meant to serve as a template for code organization, readability, structure, [other]?
Obviously you can skip using these new interfaces and roll your own with better names. There are some considerations though:
CheckPerson
isn't really a good name for its purpose, although that's subjective.Most builtin interfaces also define some other API. For example, Predicate
defines or(Predicate)
, and(Predicate)
and negate()
.
Function
defines andThen(Function)
and compose(Function)
, etc.
It's not particularly exciting, until it is: using methods other than abstract ones on functions allows for easier composition, strategy selections and many more, such as (using style suggested in this article):
Before:
class PersonPredicate {
public Predicate<Person> isAdultMale() {
return p ->
p.getAge() > ADULT
&& p.getSex() == SexEnum.MALE;
}
}
Might just become this, which is more reusable in the end:
class PersonPredicate {
public Predicate<Person> isAdultMale() {
return isAdult().and(isMale());
}
publci Predicate<Person> isAdultFemale() {
return isAdult().and(isFemale());
}
public Predicate<Person> isAdult() {
return p -> p.getAge() > ADULT;
}
public Predicate<Person> isMale() {
return isSex(SexEnum.MALE);
}
public Predicate<Person> isFemale() {
return isSex(SexEnum.FEMALE);
}
public Predicate<Person> isSex(SexEnum sex) {
return p -> p.getSex() == sex;
}
}
Although you ask "Is that it?", it's very nice that we don't have to write a new interface ever time we want type for a lambda.
Ask yourself, if you're reading an API, which is easier for a programmer to use:
public void processUsers(UserProcessor userProcessor);
... or ...
public void processUsers(Consumer<User> userProcessor);
With the former, I have to go and take a look at UserProcessor
to find out what one is, and how I could create one; I don't even know it could be implemented as a lambda until I go and find out. With the latter, I know immediately that I can type u -> System.out.println(u)
and I'll be processing users by writing them to stdout.
Also the author of the library didn't need to bloat their library with Yet Another Type.
In addition, if I coerce a lambda to a Functional Type, I can use that type's composition methods, for example:
candidates.filter( personPredicates.IS_GRADUATE.negate());
That gives you Predicate
methods and()
, or()
, negate()
; Function
methods compose()
, andThen()
-- which your custom type would not have unless you implemented them.
Java API provides many built-in Function Interfaces for java developers. and we can use the built-in Function Interfaces many times. but there two reasons to use a Customer Function Interface.
Use a Customer Function Interface to describe explicitly what's like.
let's say you having a class User
with a name parameter on the constructor.when you use the built-in Function Interface to refer the constructor the code like below:
Function<String,User> userFactory=User::new;
if you want describe it clearly you can introduce your own Function Interface, e.g:UserFactory;
UserFactory userFactory=User::new;
another reason to use Custom Function Interface due to built-in Function Interface is confused in somewhere. when you see a parameter with type Function<String,User>
,is it create a new user or query an user from database or remove the user by a string and return the user,...?if you use an exactly Function Interface you know what it doing,as an UserFactory
is create an user from a string username.
Use a Customer Function Interface to processing checked Exception in java built-in Function Interface.
Due to the built-in Function Interface can't be throwing a checked Exception,the problem occurs when you processing something in lambda expression that may be throws a checked exception,but you don't want to use the try/catch to handle the checked Exception,which will be tends to many code difficult to read in lambda expression.then you can define your own Function Interface that throws any CheckedException and adapt it to the built-in Function Interface when using java API.the code like as below:
//if the bars function throws a checked Exception,you must catch the exception
Stream.of(foos).map(t->{
try{
return bars.apply(t);
}catch(ex){//handle exception}
});
you also can define your own Function Interface throws a checked Exception,then adapt it to built-in Function,let's say it is a Mapping interface,the code below:
interface Mapping<T,R> {
R apply(T value) throws Exception;
}
private Function<T,R> reportsErrorWhenMappingFailed(Mapping<T,R> mapping){
return (it)->{
try{
return mapping.apply(it);
}catch(ex){
handleException(ex);
return null;
}
};
}
Stream.of(foos).map(reportsErrorWhenMappingFailed(bars));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With