03 - The Function Interface

It’s now time to slowly start to elaborate and dig deeper in the particular aspects of functions in Java. The language developers made our life easier.

Our first problem is easy. We want a function that returns the square of a value, like ∫(x)=x2

As we’ve seen in the introductory posts, we need a type, so we will start by writing the functional interface SimpleFunction.

SimpleFunction.java
1
2
3
4
@FunctionalInterface
public interface SimpleFunction {
public int get(int n);
}

our interface has an abstract method, that receives an int (this is how we pass values to functions) and returns a int.

With what we learned in the first and second post, we can now easily write a small program to print the square of a number by using a lambda expression,

App.java
1
2
3
4
5
6
7
public class App {

public static void main(String[] args) {
SimpleFunction square=a->a*a;
System.out.println(square.get(5));
}
}

Nothing fancy here. We created the variable square of the type SimpleFunction and assign a little code to it, in the form of a lambda expression.

If you remember the first post of the series, you might now be saying…

Wait…where are the parenthesis ? Shouldn’t we have written (a)-> return a*a ?

In the first post, we introduced rule number 1, now we are going to introduce 2 more rules,

Rule number 2

If we are passing only one argument to a function, then the parenthesis are optional.

Rule number 3

If the block of code is condensed to one line, the return keyword MUST be ommited.

Ok. I get it, but it seems quite boring to write an functional Interface every time we want to create a lambda expression.

Indeed it is, and we don’t need to do that.

Let’s focus on the get method of the SimpleFunction interface. It receives an int as argument and returns an int value. It looks like we can re-use that interface for any function that follows that pattern.

If we try the following code where the function returns the double of a value ∫(x)=2x , it still is perfectly valid,

App.java
1
2
3
4
5
6
7
public class App {

public static void main(String[] args) {
SimpleFunction twice=a->a+a;
System.out.println(twice.get(5));
}
}

If you know Generic types, some bells are probably starting to ring…

what if we change our interface to,

SimpleFunction.java
1
2
3
4
@FunctionalInterface
public interface SimpleFunction <T>{
public T get (T n);
}

Now we can reuse this interface for objects of any class,

App.java
1
2
3
4
5
6
7
8
9
10
public class App {

public static void main(String[] args) {

SimpleFunction<Double> square=a->a*a;
System.out.println(square.get(5.0));

SimpleFunction<String> firstChar=s->s.substring(0, 1);
System.out.println(firstChar.get("Lambda"));
}

Luckily Java creators where kind enough to include the java.util.Function library, which is packed with functional interfaces for the most common programming tasks, so unless we deal with some very special type of problems, this library will serve us well, and we will not need to create our own interfaces. Some of them are quite important, and, as time allows, I will try to cover them across the posts in this series.

The Function interface

1
Function <T,R> interface

Function is one of the included functional interfaces in the java.util.Funcion library.

The Function interface has one abstract method, R apply(T t);

This means that T represents the generic type of the argument entering into the function and R represents the generic type of the object coming out of the function (the return type).

We will change our code, so that it uses the Function interface instead of our own.

App.java
1
2
3
4
5
6
7
8
public class App {
public static void main(String[] args) {
Function<Integer, Integer> square=a-> a*a;
System.out.println(square.apply(10));

Function<String, String> firstChar=s->s.substring(0, 1);
System.out.println(firstChar.apply("Hello"));
}

The Function Interface is a good one to start playing with functions, passing actions to methods and understand how all this works.

The final example for today, will be something a little more elaborate, and a good training.

Problem-We have a list of person Objects. We need a method to print the person name according to a filter that should be passed to that method. We can not use Streams.

To solve this problem, the first thing we need, is the Person class,

Person.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Person {
private String name,region;
private int age;

public Person(String name, int age, String region) {
this.name=name;
this.region=region;
this.age=age;
}
//getters & setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

By using everything we’ve learned so far we can now create a PersonFilteredReportService class (remember we can not use streams as the problem stated),

PersonFilteredReportService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class PersonFilteredReportService {

public List<Person> filter(List<Person> persons, Function<Person, Boolean> filter) {
List<Person> filteredList=new ArrayList<>();
for (Person p : persons) {
if (filter.apply(p)) {
filteredList.add(p);
}
}
return filteredList;
}
public boolean print(List<Person> personList) {
for(Person p:personList) {
System.out.println(p.getName());
}
return true;
}
}

To check if this is working, we should create a unit test, but for the sake of simplicity we will code a small application with 2 different filters. One will filter by region and other will filter by age.

App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class App {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("John", 27, "Lisbon"),
new Person("Peter", 12, "Faro"),
new Person("Mary", 42, "Porto"),
new Person("Richard", 31, "Castelo Branco"),
new Person("Terry", 19, "Braga"),
new Person("Anne", 39, "Porto"));

// Defining 2 different filters using lambda expressions
Function<Person, Boolean> filterByRegion = p -> p.getRegion().equals("Porto");
Function<Person, Boolean> filterByAge = p -> p.getAge() == 12;

PersonFilteredReportService personReportService = new PersonFilteredReportService();
// Passing the lambda expression directly to the print method
personReportService.print(personReportService.filter(persons, filterByRegion));
personReportService.print(personReportService.filter(persons, filterByAge));
}
}

As you can see, the possibility of passing actions directly into methods executing them when needed, makes our code easier to understand, easier to maintain, promotes re-usability, makes the code more compact and organized…and we are only scratching the surface :)

In the next post of this series we will introduce the concept of function composition.

‘till then happy coding.

Share