[JAVA] How to create a convenient method that utilizes generics and functional interfaces

Creating a convenience method

When developing in teams with a large number of people, the processing that you want to do in common comes out. For example, character string operations and date operations are used in various scenes, but in that case, I think that you will use a convenient library or create a class with useful methods.

If it is a process for a specific class, it can be created to some extent easily, but when developing with a large number of people, there is a request to perform common process for various classes.

Something that is convenient to use at that time is "generics" or "functional interface". Even if you know about these two things, it may be difficult for some people to understand how to actually use them.

So, this time, I would like to introduce how to actually use it while following the process of actually creating a convenient method.

What is Generics (generic type)?

By passing it like a parameter to the definition of a data type, a program with a similar structure can support multiple data types.

For example, up to JDK1.4, when dealing with List, there was no type specification and anything could be entered as an Object.

List list = new ArrayList();
list.add("string");
list.add(Integer.valueOf(100));

If the type is not restricted and you want to enter only characters, the person who creates the program has to be careful. After the generics came out, it became possible to specify the type, and it became possible to write as follows.

List<String> list = new ArrayList<>();
list.add("string1");
list.add("string2");

Looking at the Java API specifications, they are listed as ** List \ <E > ** and ** ArrayList \ <E > **, respectively. This ** E ** part is called generics, and any type can be set for a program with the same structure.

What is a functional interface?

A simple representation of a functional interface is an interface that allows you to assign "method references" and "lambda expressions" introduced in Java 8.

For example, getters and setters that are often created in Java are represented by functional interfaces as follows.

Method interface Method
getter Supplier<T> T get​()
setter Consumer<T> accept(T t)

Supplier is an interface that has no arguments and returns an arbitrary type. Similar companions include IntSupplier, BooleanSupplier, etc., which return primitives rather than arbitrary ones.

Consumer is an interface that takes arguments and has no return value. There are also IntConsumer, DoubleConsumer, etc. that pass primitives to similar peers rather than arbitrarily.

Let's create a convenient method for unit testing

For those who have never used generics or functional interfaces, the above explanation may be difficult. Therefore, I will explain how to use it easily while actually creating a convenient method.

Preparation

Let's say you have created a student class. Suppose a student class has a student ID number, student name, and age, and setters and getters are defined.

public class Student {
    /**Student number*/
    private String code;

    /**Student name*/
    private String name;
    
    /**age*/
    private int age;

    //... setter,getter abbreviation...
}

To prepare the unit test data, create 3 students as test data and pack them in the List.

final Student student1 = new Student();
student1.setCode("S01001");
student1.setName("Yamada Taro");
student1.setAge(20);

final Student student2 = new Student();
student1.setCode("S02001");
student1.setName("Jiro Yamada");
student1.setAge(19);

final Student student3 = new Student();
student1.setCode("S03001");
student1.setName("Saburo Yamada");
student1.setAge(18);

final List<Student> students = new ArrayList<>();
students.add(student1);
students.add(student2);
students.add(student3);

If it's for 3 people, I can't see it yet, but if this number increases to 10 or 20, it will be a big deal.

Creating a method that simplifies the creation of a List

First, I created a method that simplifies the creation of List.

public <T> List<T> createInstanceList(Supplier<T> supplier, int size) {
    return IntStream.range(0, size)
            .mapToObj(i -> supplier.get())
            .collect(toList());
}

I will explain this program little by little.

public <T> List<T> createInstanceList(Supplier<T> supplier, int size) 

First of all, regarding the method declaration, \ <T > and generics are defined. Since it uses generics, it has become a method that can be used for any class.

Since the return type is ** List \ <T > **, a List of any type will be returned.

Since the first argument is declared ** Supplier \ <T > **, you can receive an interface just to return an arbitrary type as mentioned in the introduction of generics.

The first argument is ** int **, which allows you to specify the size of the List.

IntStream.range(0, size)

Next is the value to be returned. First, the stream is created with ** IntStream.range (0, size) **. It repeats from 0 to size -1 because it uses range. If size is 3, the numbers 0, 1, 2 are repeated.

.mapToObj(i -> supplier.get())

I've just passed 0, 1, 2 from IntStream, but I'm ignoring it and using ** supplier.get () **. This calls the get method of the function interface passed as an argument and returns the result.

.collect(toList());

Finally, the number of repetitions of the number (3 times in the example) returns the value received from the functional interface in a List.

Apply List simplification method

Since there are some parts that are difficult to explain, let's change the first code to see how it changes when actually using this method.

final List<Student> students = createInstanceList(Student::new, 3);
students.get(0).setCode("S01001");
students.get(0).setName("Yamada Taro");
students.get(0).setAge(20);
students.get(1).setCode("S02001");
students.get(1).setName("Jiro Yamada");
students.get(1).setAge(19);
students.get(2).setCode("S03001");
students.get(2).setName("Saburo Yamada");
students.get(2).setAge(18);

The student instantiation and set to List are gone.

final List<Student> students = createInstanceList(Student::new, 3);

This first line creates a List and an internal instance at the same time. ** Student :: new ** is said to be a ** constructor reference ** and returns a ** functional interface ** that returns the result of new Student ().

The constructor reference will be treated as a Supplier and will be passed as the first argument of the method created earlier. The supplier.get () that was in the previous method will return the result of new Student ().

Create a method to set a value for an object in List

I created the method earlier and the code is shorter, but setting the value is still complicated. Therefore, I will write the following code.

public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values) {
    for (int i = 0; i < obj.size(); i++) {
        biConsumer.accept(obj.get(i), values[i]);
    }
}

I will explain this program little by little.

public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values)

First of all, regarding the method declaration, ** \ <T, U > ** and generics are defined. Unlike the first method, it handles two arbitrary types.

Since the return type is ** void **, no value is returned.

Since the first argument is ** List obj **, pass a list of any type for which you want to set a value.

The second argument is ** BiConsumer <T, U> biConsumer **. This will be explained later.

The third argument is ** U ... values **, which allows you to pass variable arguments. With this argument, you can make the value you want to set for the objects in the List variable.

Apply method for value set

Let's change the code to see how it changes when we actually use this method.

final List<Student> students = createInstanceList(Student::new, 3);
setValues(students, Student::setCode, "S01001", "S02001", "S03001");
setValues(students, Student::setName, "Yamada Taro", "Jiro Yamada", "Saburo Yamada");
setValues(students, Student::setAge, 20, 19, 18);

The code is much shorter and the outlook is better.

I want to set a value for the first argument ** I have a list of students **.

We are passing ** setter's method reference ** as the second argument. This method reference is passed as BiConsumer \ <T, U > for the method we just created and is called by biConsumer.accept (...).

Since the third and subsequent arguments are ** variable arguments **, list the values you want to set.

Class method reference and instance method reference

I have one question here. The setter method should normally use ** Consumer \ <T > ** as it has one argument and no return value. Now, I will briefly explain why we use a functional interface called BiConsumer that takes two arguments.

For example, if you use Conumer \ <T > as an argument, you can create the following program.

public <T> void setValue(Consumer<T> consumer, T value) {
    consumer.accept(value);
}

Here's how to use it.

Student student = new Student();
setValue(student::setCode, "S01001");

The differences from using BiConsumer are as follows.

interface Method reference
BiConsumer<T, U> Student::setCode
Consumer<T> student::setCode

When using BiConsumer, the method reference of the class is used, and when using Consumer, ** the method reference of the instance ** is used.

It doesn't make sense to pass a method reference for each instance inside the List because we want to set the value for each instance in the List this time. Therefore, I pass ** class method reference ** and call setter for each instance in List.

Summary

The following two methods were created this time.

public <T> List<T> createInstanceList(Supplier<T> supplier, int size) {
    return IntStream.range(0, size)
            .mapToObj(i -> supplier.get())
            .collect(toList());
}

public <T, U> void setValues(List<T> obj, BiConsumer<T, U> biConsumer, U... values) {
    for (int i = 0; i < obj.size(); i++) {
        biConsumer.accept(obj.get(i), values[i]);
    }
}

I usually work on development with many students. Since there are large differences in skills, it is essential to be able to write similar programs as much as possible and to create convenient methods to improve development efficiency.

You can quickly create simple and convenient methods, but the more general you try to create, the more convenient you will be if you know the generics and functional interfaces introduced here.

Recommended Posts

How to create a convenient method that utilizes generics and functional interfaces
How to create a method
How to test a private method in Java and partially mock that method
How to create a class that inherits class information
Find a value that is convenient to have a method and make it a ValueObject
How to create and execute method, Proc, Method class objects
How to create and launch a Dockerfile for Payara Micro
[Swift5] How to create a .gitignore file and the code that should be written by default
How to create a Maven repository for 2020
[Swift5] How to create a splash screen
[rails] How to create a partial template
A simple and convenient method for HashMap
How to quickly create a reverse proxy that supports HTTPS with Docker
How to create a validator that allows only input to any one field
How to create a server executable JAR and WAR with Spring gradle
How to operate IGV using socket communication, and the story of making a Ruby Gem using that method
How to create a database for H2 Database anywhere
[Rails] How to create a graph using lazy_high_charts
[Ruby] How to use gsub method and sub method
How to create pagination for a "kaminari" array
How to create a theme in Liferay 7 / DXP
[1st] How to create a Spring-MVC framework project
How to easily create a pull-down in Rails
[Rails] How to create a Twitter share button
[Spring sample code included] How to create a form and how to get multiple records
How to make a Vagrant Plugin that you learned when you forked and published vagrant-mutagen
How to create a header or footer once and use it on another page
I tried using Wercker to create and publish a Docker image that launches GlassFish 5.
How to create a Java environment in just 3 seconds
How to create a JDBC URL (Oracle Database, Thin)
How to create a Spring Boot project in IntelliJ
How to create a data URI (base64) in Java
How to mock a super method call in PowerMock
How to convert A to a and a to A using AND and OR in Java
[Apple Subscription Offer] How to create a promotional offer signature
How to create docker-compose
How to create an environment that uses Docker (WSL2) and Vagrant (VirtualBox) together on Windows
Java conditional branching: How to create and study switch statements
How to create a lightweight container image for Java apps
How to create a form to select a date from the calendar
How to create a placeholder part to use in the IN clause
I want to call a method and count the number
Create a method that can retrieve characters from any location
Create a method to return the tax rate in Java
How to develop and register a Sota app in Java
How to create a service builder portlet in Liferay 7 / DXP
How to create an application
How to leave a comment
Create a java method [Memo] [java11]
How to insert a video
How to create a jar file or war file using the jar command
How to create a registration / update function where the table crosses
[Rails 6] How to create a dynamic form input screen using cocoon
How to POST JSON in Java-Method using OkHttp3 and method using HttpUrlConnection-
How to implement a job that uses Java API in JobScheduler
How to create a new Gradle + Java + Jar project in Intellij 2016.03
How to deploy an app that references a local jar to heroku
How to load a Spring upload file and view its contents
How to read a file and treat it as standard input
[Convenient to remember !!!] How to convert from LocalDate type to character string and from character string to LocalDate type
How to store the information entered in textarea in a variable in the method