Thumbnail image

Improve your Java code using - Stream API - Part 3

25/1/2024 4-minute read

In this third part of the Stream Api series, let’s talk about the method references and explore its types.

As name suggests, they are similar to object references, but references to a method as well.

As you already know, we can pass behavior as parameters. But, you keep in your mind, there is no difference between method and lambda expression. So, method references are shortened versions of lambda expressions tha call a specific method.

Let’s say you have a Consumer as defined below:

Consumer<String> consumer = str -> System.out.println(str);

This can be written as:

Consumer<String> consumer = System.out::println;

Let’s take a look into another example. Now we have a Function<T,R>functional interface as defined

Function<People, Integer> function = f -> f.getAge();

This can be written as:

Function<People, Integer> function = People::getAge;

4 types of method references

1. Static Methods

This is the syntax for a static methods as method reference:

ClassName::MethodName

To make it more explicit and easy to understand, let’s take a look a new example. In the below example, we have a method getLength() which returns the length of the String.

import java.util.ArrayList;
import java.util.List;

public class StreamMethodRef {

    public static int getLength(String str){
        return str.length();
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("work");
        list.add("read");
        list.add("practice");
        list.add("write");

        // Code without using method reference.
        list.stream()
                .mapToInt(str -> StreamMethodRef.getLength(str))
                .forEach(System.out::println);

        // Code with method reference.
        list.stream()
                .mapToInt(StreamMethodRef::getLength)
                .forEach(System.out::println);

    }
}

2. Instance method of a particular object

Instance method as a method reference have this syntax

ReferenceVariable::MethodName

Let’s take a look at the same example, but now the getLength() method is not static.

import java.util.ArrayList;
import java.util.List;

public class StreamMethodRef {

    public int getLength(String str){
        return str.length();
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("work");
        list.add("read");
        list.add("practice");
        list.add("write");

        StreamMethodRef example = new StreamMethodRef();
        // Code without using method reference.
        list.stream()
                .mapToInt(str -> StreamMethodRef.getLength(str))
                .forEach(System.out::println);

        // Code with method reference.
        list.stream()
                .mapToInt(StreamMethodRef::getLength)
                .forEach(System.out::println);

    }
}

3. Instance method of an arbitrary object

This type of reference method can directly use the class name in the method reference. He does not require the object of the referenced class.

import java.util.ArrayList;
import java.util.List;

public class StreamMethodRef {

    public int getLength(String str) {
        return str.length();
    }

    public static void main(String[] args) {
        List<Employee> list = new ArrayList<>();
        list.add(new Employee("Sergio", 23, 20000));
        list.add(new Employee("Ruy", 18, 40000));
        list.add(new Employee("Julia", 54, 100000));
        list.add(new Employee("Andressa", 5, 34000));
        list.add(new Employee("Ashley", 63, 54000));
        // Code without using method reference
        int totalSalary1 = list.stream()
                .mapToInt(emp -> emp.getSalary())
                .sum();
        
        // Code with method reference
        int totalSalary = list.stream()
                .mapToInt(Employee::getSalary)
                .sum();

        System.out.println("The total salary is " + totalSalary);
    }
}


class Employee {
    String name;
    int age;
    int salary;

    Employee(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

4. Constructor references

With a new key word, we can refer to a constructor in the same way we reference a static method.

import java.util.ArrayList;
import java.util.List;

public class StreamMethodRef {

    public int getLength(String str) {
        return str.length();
    }

    public static void main(String[] args) {
        List<Employee> list = new ArrayList<>();
        list.add("Sergio");
        list.add("Ruy");
        list.add("Julia");
        list.add("Andressa");
        list.add("Dora");

        // Code without constructor reference
        list.stream()
                .map(name -> new Employee(name))
                .forEach(System.out::println);
        
        // Code with constructor reference
        list.stream()
                .map(Employee::new)
                .forEach(System.out::println);
    }
}


class Employee {
    String name;
    int age;
    int salary;
    
    Employee(String name) {
        this.name = name;
    }

    Employee(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

Conclusion

Method references in Java provide a clear, and concise way to represent method calls within the scope of functional interfaces. Not only does this enhance the readability of your code, but it also simplifies the syntax. Remember, practice is the key to mastering these concepts.

As always, happy coding!

Posts in this Series