×
Community Blog Java Programming Skills – Boilerplate Code

Java Programming Skills – Boilerplate Code

In this article, we will introduce several common Java boilerplate codes with the hopes of encouraging you to build and improve your own boilerplate code base.

1

Preface

The Northern Song Dynasty scientist Shen Kuo described movable type printing in the 18th volume Skills of the Brush Talks from Dream Brook as follows:

During the Qingli years, there was a commoner called Bi Sheng who invented movable type printing. The method is to carve Chinese characters with clay. The carved characters are as thin as a copper. Each character is carved as a mold, which is later baked hard with fire ... Such printing method may not be simple for two or three copies, but it is extremely effective for hundreds or thousands of copies.

Similarly in coding and software development, we can sum up many boilerplate codes, which are just like movable types in movable type printing. When writing new codes, programmers would copy, modify and replace the boilerplate codes as needed. In this manner, coding becomes extremely fast. Boilerplate codes can help developers make formats more standardized, improve the quality of codes, and increasing the speed of coding.

In this article, we will introduce several common Java boilerplate codes. This article intends to encourage you to continue to summarize, improve, and build your own boilerplate code base.

1. Introduction to Boilerplate Codes

1.1 What Is Boilerplate Code?

A boilerplate code usually refers to a pile of code blocks with a fixed pattern that can be widely applied to various program modules.

For example, “read file” is a typical boilerplate code:

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
    String line;
    while (Objects.nonNull(line = reader.readLine())) {
        // process a line
        ...
    }
} catch (IOException e) {
    String message = String.format("read file(%s) exception", fileName);
    log.error(message, e);
    throw new ExampleException(message, e);
}

1.2 What Are the Boilerplate Code for?

A boilerplate can be understood from two aspects: example and pattern. Example indicates that boilerplate can be regarded as a kind standard example. Pattern means boilerplate can be used as a solution. For a similar instance, it can be solved easily through copying and modifying the boilerplate code as needed.

The main functions of boilerplate code include:

  1. Provide a standard example: It allows beginners to quickly get started for usage.
  2. Provide a solution: In similar instances, you can quickly use this solution.
  3. Help accumulate experience: When a boilerplate code is found, continuous optimizations are made to achieve the optimal example.
  4. Help improve code quality: Boilerplate codes must have passed the test of time, and the probability of bugs and errors is relatively low.
  5. Help improve coding speed: You can copy, paste and modify boilerplate codes to greatly improve your coding speed.
  6. Help unify code style: Boilerplate codes ensure consistent code style written each time.

1.3 How to Write Boilerplate Code?

For this part, there are many detailed explanations and examples but we will not be presenting them here. Among them, the coding methods suitable for boilerplate code are as follows:

1.  Copy and Paste to Generate Code

Copying and pasting boilerplate codes properly will make your coding more efficient.

2.  Replace Texts to Generate Code

You can quickly generate a new piece of code by using text replacement.

3.  Use Excel Formulas to Generate Code

You can first formulate boilerplate codes and pass into different parameters to generate different codes.

4.  Use Tools or Plug-ins to Generate Code

Many development tools or plug-ins provide tools to generate code, for example, the constructor method, the overloaded base class/interface method, the Getter/Setter method, the toString method, the database access method ... They can help avoid a lot of manual coding.

5.  Use Code to Generate Code

It means writing boilerplate codes by yourself and generating codes with your own boilerplate codes.

1.4 How to Reduce Boilerplate Code?

Boilerplate codes are highly repetitive, and they are generally considered redundant but necessary codes. In fact, it is not true. Some boilerplate codes cannot be reduced just because we have not found suitable solutions. Typically, you can reduce boilerplate codes in the following ways:

1.4.1 Use Annotations to Reduce Boilerplate Code

For example, the Getter/Setter in the JavaBean model class is a boilerplate code. We can reduce such boilerplate code by using the @Getter/@Setter annotation of Lombok.

Original code:

public class User {
    private Long id;
    ...
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    ...
}

Optimized code:

@Getter
@Setter
public class User {
    private Long id;
    ...
}

1.4.2 Use Framework to Reduce Boilerplate Code

For example, MyBatis is an excellent persistence layer framework that encapsulates all JDBC operations, such as acquiring database connections and statements, setting parameters, and acquiring result sets. MyBatis can configure and map original types, interfaces, and Plain Old Java Objects (Java POJO) into the records in the database through simple XML or annotations.

Original code:

/** Query company employees */
public List< EmployeeDO> queryEmployee(Long companyId) {
    try (Connection connection = tddlDataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(QUERY_EMPLOYEE_SQL)) {
        statement.setLong(1, companyId);
        try (ResultSet result = statement.executeQuery()) {
            List< EmployeeDO> employeeList = new ArrayList<>();
            while (result.next()) {
                EmployeeDO employee = new EmployeeDO();
                employee.setId(result.getLong(1));
                employee.setName(result.getString(2));
                ...
                employeeList.add(employee);
            }
            return employeeList;
        }
    } catch (SQLException e) {
        String message = String.format("query user exceptions of the company(%s)", companyId);
        log.error(message, e);
        throw new ExampleException(message, e);
    }
}

Optimized code:

UserDAO.java:

@Mapper
public interface UserDAO {
    List< EmployeeDO> queryEmployee(@Param("companyId") Long companyId);
}

UserDAO.xml:

< mapper namespace="com.example.repository.UserDAO">
    < select id="queryEmployee" resultType="com.example.repository.UserDO">
        select id
        , name
        ...
        from t_user
        where company_id = #{companyId}
    < /select>
< /mapper>

1.4.3 Use Design Pattern to Reduce Boilerplate Code

The design pattern can encapsulate some repetitive codes. For example, the preceding code of “reading file” line can be encapsulated by using the template method.

Original code:

try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
    String line;
    while (Objects.nonNull(line = reader.readLine())) {
        // process a line
        ...
    }
} catch (IOException e) {
    String message = String.format("read file(%s) exception", fileName);
    log.error(message, e);
    throw new ExampleException(message, e);
}

Optimized code:

/** Define method */
public static void readLine(String fileName, Consumer< String> lineConsumer) {
    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
        String line;
        while (Objects.nonNull(line = reader.readLine())) {
            lineConsumer.accept(line);
        }
    } catch (IOException e) {
        String message = String.format("read file (%s) exception", fileName);
        log.error(message, e);
        throw new ExampleException(message, e);
    }
}

// Use code
readLine("example.txt", line -> {
    // Process a line
    ...
});

1.5 Impregnable Boilerplate Code

There will be no boilerplate codes in the world if they can be destroyed. Even the methods of reducing boilerplate codes provided in the previous section cannot completely eliminate boilerplate codes, because they still exist in the implementation of the framework and pattern. Therefore, boilerplate codes cannot be eliminated.

Now that we cannot eliminate boilerplate codes, we need to make proper use of them. If a refined boilerplate code is only used two or three times, it is not convenient. However, if it is used hundreds or thousands times, it is extremely time-efficient. The following lists several common Java boilerplate codes and describes how to refine and use boilerplate codes in daily programming.

2. Define Utility Classes

2.1 Common Definition Method

Typically, we define a utility class as follows:

/** Example utility class */
public class  ExampleHelper {
    /** Constant value */
    public final  static  int CONST_VALUE = 123;
    /** Sum method */
    public  static  int  sum( int a, int b) {
        return a + b;
    }
}

2.2 Problems

2.2.1 Improper Modifier Order

When scanning the preceding code with the SonarLint plug-in, the following problem occurs:

Rule key Rule name Description
java:S1124 Modifiers should be declared in the correct order. Reorder the modifiers to comply with the Java Language Specification.

The Java Language Specification recommends "static final" instead of "final static". Remember this rule: static constant, static before constant.

2.2.2 Utility Classes Can Be Inherited and Overwritten

Define MyExampleHelper to inherit ExampleHelper:

public class MyExampleHelper extends ExampleHelper {
    /** constant value */
    public static final int CONST_VALUE = 321;

    /** Sum method */
    public static int sum(int a, int b) {
        return a * b;
    }
}

You will find that MyExampleHelper overwrites the constants and methods in ExampleHelper. As a result, you do not know whether the constants and methods in ExampleHelper are used.

Many of you like to define a utility class with the same name as that of the utility class provided by Apache, make this utility class inherit the Apache utility class and add your own implementation methods in this class. In fact, it is not recommended because you don’t know whether it’s the constants and methods provided by the Apache utility class or those that are overwrite that you are calling. The best way is to add the keyword “final” to the utility class so that it cannot be inherited or overwritten.

2.2.3 Utility Classes Can Be Instantiated

For the ExampleHelper utility class, we can use it as follows:

int value = ExampleHelper.CONST_VALUE;
int sum = ExampleHelper.sum(1, 2);

It can also be used like this:

ExampleHelper exampleHelper = new ExampleHelper();
int value = exampleHelper.CONST_VALUE;
int sum = exampleHelper.sum(1, 2);

For utility classes, instantiation is not necessary. Therefore, we recommend that you add a private constructor and throw an UnsupportedOperationException in the method.

2.3 The Best Definition Method

Based on the preceding problems and their solutions, the ExampleHelper utility class is best defined as follows:

/** Example utility class */
public final class ExampleHelper {
    /** Constant value */
    public static final int CONST_VALUE = 123;

    /** Constructor */
    private ExampleHelper() {
        throw new UnsupportedOperationException();
    }

    /** Sum method */
    public static int sum(int a, int b) {
        return a + b;
    }
}

3. Define Enumeration Classes

3.1 Common Definition Method

Generally, enumeration classes are defined as follows:

/** Example enumeration class */
public  enum ExampleEnum {
    /** Enumeration-related */
    ONE ( 1, "one(1)"),
    TWO ( 2, "two(2)"),
    THREE ( 3, "two(3)");

    /** Attribute-related */
    private Integer value;
    private String desc;

    /** Constructor */
    private  ExampleEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    /** Acquire the value */
    public Integer getValue() {
        return value;
    }

    /** Acquire the description */
    public String getDesc() {
        return desc;
    }
}

3.2 Optimization Suggestions

3.2.1 The Modifier “Private” Can Be Omitted

When scanning the preceding code with the SonarLint plug-in, the following problem occurs:

Rule key Rule name Description
java:S2333 Redundant modifiers should not be used. "Private" is redundant in this context.

As suggested, the redundant “private” modifiers before the constructor should be removed.

3.2.2 A Basic Date Type Is Recommended

It is not problematic to use the wrapper class Integer to save the enumerated value. However, basic types are always considered as the priority, so the basic type int is recommended.

3.2.3 A Final Field Is Recommended

Assuming that you want to implement a static method, and you accidentally modify the enumerated value:

 
/** Modify the value */
public static void modifyValue() {
    for (ExampleEnum value : values()) {
        value.value++;
    }
}

If the modifyValue method is called, the enumerated value is modified, causing an error in the application. To avoid this issue, we recommend that you add the modifier “final” to fields to avoid malicious tampering.

3.3 The Best Definition Method

/** Example enumeration class */
public enum ExampleEnum {
    /** Enumeration-related */
    ONE(1, "one(1)"),
    TWO(2, "two(2)"),
    THREE(3, "two(3)");

    /** Field-related */
    private final int value;
    private final String desc;

    /** Constructor */
    ExampleEnum(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    /** Acquire the value */
    public int getValue() {
        return value;
    }

    /** Acquire the description */
    public String getDesc() {
        return desc;
    }
}

4. Define Model Classes

The following examples describe the definition method, advantages and disadvantages of user models by using the JavaBean pattern, constructor overloading, and Builder mode.

Assumptions: The user model has four attributes: identity, name, age, and description. Among them, identity and name are required and age and description are non-required.

4.1 JavaBean Pattern

JavaBean is a Java class that follows a specific writing method. It usually has the following characteristics:

  1. It must have a constructor without parameters.
  2. All attribute fields must be private.
  3. All attribute fields must be opened through the Getter/Setter method that follows a naming specification.

The following user model class is defined through the JavaBean pattern:

/** User class */
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String desc;

    public Long getId() {return id;}
    public void setId(Long id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public Integer getAge() {return age;}
    public vid setAge(Integer age) {this.age = age;}
    public String getDesc() {return desc;}
    public void setDesc(String desc) {this.desc = desc;}
}

Note: You can also use the @Getter/@Setter annotation of Lombok to generate corresponding Getter/Setter methods.

Code for use:

User user = new User();
user.setId(1L);
user.setName("alibaba");
user.setAge(102);
user.setDesc("test");
verifyUser(user);

Advantages:

  1. The code is simple, including only private attribute fields and public Getter/Setter methods.
  2. The code of the assignment object is readable and you can clearly see the corresponding values of the attribute fields.
  3. It is simple and practical. Therefore, it is widely used in middleware such as HSF, Dubbo, and MyBatis.

Disadvantages:

  1. Attribute fields can be set through the Setter method, so they cannot be defined as immutable classes.
  2. Since each field is set separately, it is not guaranteed that the fields are all required and they must be verified after the setting is completed.

4.2 Constructor Overloading

The following is a user model class defined through the constructor overloading:

/** User class */
public final class User {
    private Long id;
    private String name;
    private Integer age;
    private String desc;

    public User(Long id, String name) {
        this(id, name, null);
    }
    public User(Long id, String name, Integer age) {
        this(id, name, age, null);
    }
    public User(Long id, String name, Integer age, String desc) {
        Assert.notNull(id, "ID cannot be empty");
        Assert.notNull(name, "Name cannot be empty");
        this.id = id;
        this.name = name;
        this.age = age;
        this.desc = desc;
    }

    public Long getId() {return id;}
    public String getName() {return name;}
    public Integer getAge() {return age;}
    public String getDesc() {return desc;}
}

Code for use:

User user1 = new User(1L, "alibaba");
User user2 = new User(1L, "alibaba", 102, "test");

Advantages:

  1. The code of the initialization object is simple, with only one line.
  2. You can define it as an immutable class. After initialization, the value of attribute fields cannot be changed.
  3. Validation of non-nullable attribute can be performed within the constructor.

Disadvantages:

  1. There are excessive constructor overloading methods, which makes it hard to cover all combinations of required fields and non-required fields.
  2. The poor readability of the initialization object code makes it hard to find the corresponding relations between the attribute fields and values.
  3. If you delete a field, the initialization object code may not return an error, resulting in an assignment error.

4.3 Builder Mode

/** User class */
public final class User {
    private Long id;
    private String name;
    private Integer age;
    private String desc;

    private User(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
        this.desc = builder.desc;
    }
    public static Builder newBuilder(Long id, String name) {
        return new Builder(id, name);
    }

    public Long getId() {return id;}
    public String getName() {return name;}
    public Integer getAge() {return age;}
    public String getDesc() {return desc;}

    public static class Builder {
        private Long id;
        private String name;
        private Integer age;
        private String desc;

        private Builder(Long id, String name) {
            Assert.notNull(id, "ID cannot be empty");
            Assert.notNull(name, "Name cannot be empty");
            this.id = id;
            this.name = name;
        }
        public Builder age(Integer age) {
            this.age = age;
            return this;
        }
        public Builder desc(String desc) {
            this.desc = desc;
            return this;
        }
        public User build() {
            return new User(this);
        }
    }
}

Note: You can use the @Builder annotation of Lombok to simplify the code.

Code for use:

User user = User.newBuilder(1L, "alibaba").age(102).desc("test").build();

Advantages:

  1. The required and non-required parameters are clarified and verified in the constructor.
  2. It can be defined as an immutable class. After initialization, the value of attribute fields cannot be changed.
  3. The good readability of the assignment code makes it clear the corresponding relations between attribute fields and values.
  4. It supports calls through method chaining, the code of which is simpler than that of calling through the Setter method.

Disadvantages:

  1. The amount of code is large, with one more Builder class, one more attribute field defined and one more assignment method implemented.
  2. The operation efficiency is low. You need to create a Builder instance, assign a value to the attribute field, create a target instance, and finally copy the attribute field.

5. Define Collection Constants

Collection constants such as list constants, set constants, and map constants are often used in coding.

5.1 Common Definition Method

Definition Code:

The simplest method is to directly define a common set constant.

/** Example utility class */
public final class ExampleHelper {
    /** Constant value list */
    public static final List< Integer> CONST_VALUE_LIST = Arrays.asList(1, 2, 3);
    /** Constant value collection */
    public static final Set< Integer> CONST_VALUE_SET = new HashSet<>(Arrays.asList(1, 2, 3));
    /** Constant value mapping */
    public static final Map< Integer, String> CONST_VALUE_MAP;
    static {
        CONST_VALUE_MAP = new HashMap<>(MapHelper.DEFAULT);
        CONST_VALUE_MAP.put(1, "value1");
        CONST_VALUE_MAP.put(2, "value2");
        CONST_VALUE_MAP.put(3, "value3");
    }
    ...
}

Code for use:

It is also very convenient to directly use the code through "class name.constant name”.

// Use a constant value collection
List< Integer> constValueList = ExampleHelper.CONST_VALUE_LIST;
Set< Integer> constValueSet = ExampleHelper.CONST_VALUE_SET;
Map< Integer, String> constValueMap = ExampleHelper.CONST_VALUE_MAP;

5.2 Problems

When scanning the preceding code with the SonarLint plug-in, the following problem occurs:

Rule key Rule name Description
java:S2386 Mutable fields should not be "public static". Make this member "protected ".

Since common collection objects such as ArrayList, HashMap, and HashSet are mutable collection objects, even if they are defined as static constants, they can be modified by using operation methods. Therefore, the collection constant defined by the preceding method is not a real set constant. Among them, the internal ArrayList generated by the Arrays.asList method cannot execute the add/remove/clear method. However, it can set method and is a mutable collection object.

// Operate constant list
ExampleHelper.CONST_VALUE_LIST.remove ( 3); // UnsupportedOperationException
ExampleHelper.CONST_VALUE_LIST.add ( 4); // UnsupportedOperationException
ExampleHelper.CONST_VALUE_LIST.set ( 1, 20); // [1,20,3]
ExampleHelper.CONST_VALUE_LIST.clear(); // UnsupportedOperationException

// Operate constants collection
ExampleHelper.CONST_VALUE_SET.remove ( 3); // [1,2]
ExampleHelper.CONST_VALUE_SET.add ( 3); // [1,2,3]
ExampleHelper.CONST_VALUE_SET.clear(); // []

// Operate constant mapping
ExampleHelper.CONST_VALUE_MAP.remove ( 3); // {1:"value1",2:"value2"}
ExampleHelper.CONST_VALUE_MAP.put ( 3, "value3"); // {1:"value1",2:"value2",3:"value3"}
ExampleHelper.CONST_VALUE_MAP.clear(); // []

5.3 The Best Definition Method

In JDK, the collections utility class provides a method to change a mutable collection object into an immutable collection object. The immutable collection object cannot be modified and an UnsupportedOperationException will be thrown during modification. Therefore, you can use this method to define static constants in the collection.

/** Example utility class */
public final class ExampleHelper {
    /** Constant value list */
    public static final List< Integer> CONST_VALUE_LIST = Collections.unmodifiableList(Arrays.asList(1, 2, 3));
    /** Constant value collection */
    public static final Set< Integer> CONST_VALUE_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));
    /** Constant value mapping */
    public static final Map< Integer, String> CONST_VALUE_MAP;
    static {
        Map< Integer, String> valueMap = new HashMap<>(MapHelper.DEFAULT);
        valueMap.put(1, "value1");
        valueMap.put(2, "value2");
        valueMap.put(3, "value3");
        CONST_VALUE_MAP = Collections.unmodifiableMap(valueMap);
    }
    ...
}

6. Define Array Constants

The previous section describes how to define set constants, and this section describes how to define array constants.

6.1 Define Public Array Constants

Definition Code:

Generally, when defining an array constant, you will define a public array constant as follows:

/** Example utility class */
public final class ExampleHelper {
    /** Constant value array*/
    public static final int[] CONST_VALUES = new int[] {1, 2, 3};
    ...
}

Code for use:

It is also very convenient to use the code. You can directly use it through the "class name.constant name”.

// Use constant value array
int [] constValues = ExampleHelper.CONST_VALUES;

Problems:

However, the array value can be modified with subscript so that the value of the array constants may become mutable. Therefore, the array constant defined by this method is not a real array constant.

// Modify constant value array
ExampleHelper.CONST_VALUES [1] = 20; // [1, 20, 3]    

6.2 Define Public Collection Constants

Definition Code:

A public collection constant can be returned through the method of defining collection constants in the previous chapter.

/** Example utility class */
public  final  class  ExampleHelper{
    /** Constant value list */
    public  static  final List< Integer> CONST_VALUE_LIST =
        Collections.unmodifiableList(Arrays.asList (1, 2, 3);
    ...
}

Code for use:

You can convert the collection constant into the array constant.

// Use constant value list
int[] constValues = ExampleHelper.CONST_VALUE_LIST.stream()
.mapToInt(Integer::intValue).toArray();

Problems:

Each time the collection constant is converted into the array constant, thus reducing the efficiency of program running.

6.3 The Best Definition Method

The best solution is "private array constant + public cloning method”. As shown in the following code, you can define a private array constant to prevent it from being used by external classes. Then, define a method to obtain the array constant and return the cloned value of the array constant.

Definition Code:

Here is a solution of "private array constant + public cloning method". As shown in the following code, you can define a private array constant to prevent it from being used by external classes. Then, define a method to obtain array constant and return the cloned value of the array constant.

/** Example utility class */
public  final  class  ExampleHelper {
    /** Constant value array */
    private  static  final  int[] CONST_VALUES = new  int[] { 1, 2, 3};
    /** Method to acquire the array of constant value */
    public  static  int[] getConstValues() {
        return CONST_VALUES.clone();
    }
    ...
}

Code for use:

Since each time a clone array is returned, even if the constant value of the clone array is modified, the constant value of the original array is not modified.

// Use constant value method
int[] constValues = ExampleHelper.getConstValues(); // [1, 2, 3]
constValues[1] = 20; // [1, 20, 3]
constValues = ExampleHelper.getConstValues(); // [1, 2, 3]

7. Define Multi-conditional Expressions

7.1 Use the Operator & & (or ||) for Direct Concatenation

Definition Code:

Sometimes, we need to judge many conditions and use && (or ||) to concatenate multiple conditional expressions.

/** Method to acquire audit results */
private static Integer getAuditResult(AuditDataVO data) {
    if (isPassed(data.getAuditItem1())
        && isPassed(data.getAuditItem2())
        ...
        && isPassed(data.getAuditItem11())) {
        return AuditResult.PASSED;
    }
    return AuditResult.REJECTED;
}

Problems:

If scanning the preceding code with the SonarLint plug-in, two problems may occur:

Rule key Rule name Description
java:S1067 Expressions should not be too complex. Reduce the number of conditional operators (11) used in the expression (maximum allowed 3).
java:S1541 Methods should not be too complex. The Cyclomatic Complexity of this method "getAuditResult" is 13 which is greater than 10 authorized.

Cyclomatic complexity (CC), also called conditional complexity, is a measure of code complexity represented by V(G).

One testing strategy, called basis path testing by McCabe who first proposed it, is to test each linearly independent path through the program; in this case, the number of test cases will equal the cyclomatic complexity of the program.

CC can be used to measure the complexity of the module decision structure. The complexity is reflected in the number of linearly independent paths, which can also be understood as the least number of test instances that can cover all possible situations.

7.2 Use the Operators = and && (or ||) for Concatenation

Definition Code:

Separate && (or ||), and cascade by using = and && (or ||).

/** Method to acquire audit results */
private static AuditResult getAuditResult(AuditDataVO data) {
    boolean isPassed = isPassed(data.getAuditItem1());
    isPassed = isPassed && isPassed(data.getAuditItem2());
    ...
    isPassed = isPassed && isPassed(data.getAuditItem11());
    if (isPassed) {
        return AuditResult.PASSED;
    }
    return AuditResult.REJECTED;
}

Problems:

When scanning the preceding code with the SonarLint plug-in, one problem occurs:

Rule key Rule name Description
java:S1541 Methods should not be too complex. The Cyclomatic Complexity of this method "getAuditResult" is 13 which is greater than 10 authorized.

It means the cascading operators = and && (or ||) cannot reduce the CC of the method.

7.3 Use the List of Dynamic Parameterless Lambda Expressions

Definition Code:

The following code uses a list of dynamic parameterless Lambda expressions for optimization. Each conditional expression is stored in the list as a BooleanSupplier object, and then the conditional expression is executed in sequence to obtain the final result.

/** Method to acquire the audit results */
private static AuditResult getAuditResult(AuditDataVO data) {
    List< BooleanSupplier> supplierList = new ArrayList<>();
    supplierList.add(() -> isPassed(data.getAuditItem1()));
    supplierList.add(() -> isPassed(data.getAuditItem2()));
    ...
    supplierList.add(() -> isPassed(data.getAuditItem11()));
    for (BooleanSupplier supplier : supplierList) {
        if (!supplier.getAsBoolean()) {
            return AuditResult.REJECTED;
        }
    }
    return AuditResult.PASSED;
}

Problems:

When scanning the preceding code with the SonarLint plug-in, no problems occur. However, adding dynamic Lambda expressions every time results in low program efficiency. Then, is there a way to make the Lambda expressions static?

7.4. Use the List of Static Parameter Lambda Expressions

Definition Code:

To solidify Lambda expressions, you must dynamically pass in an AuditDataVO object. Here we adopt Predicate to receive the Lambda expression. In the Lambda expression, it specifies the AuditDataVO object data. Then, in the for loop, it specifies AuditDataVO object data in turn and calculate the value of the expression.

/** Assertion list of audit results */
private static final List< Predicate<AuditDataVO>> AUDIT_RESULT_PREDICATE_LIST =
    Collections.unmodifiableList(Arrays.asList(
        data -> isPassed(data.getAuditItem1()),
        data -> isPassed(data.getAuditItem2()),
        ...
        data -> isPassed(data.getAuditItem11())));

/** Method to acquire audit results */
private static AuditResult getAuditResult(AuditDataVO data) {
    for (Predicate< AuditDataVO> predicate : AUDIT_RESULT_PREDICATE_LIST) {
        if (!predicate.test(data)) {
            return AuditResult.REJECTED;
        }
    }
    return AuditResult.PASSED;
}

Applicable conditions:

  1. It is applicable when && (or ||) concatenates a large number of conditional expressions;
  2. It is applicable for scenarios where all conditional expressions need the same parameter. If different parameters are passed to each conditional expression, you can only use the list of dynamic parameterless Lambda expression;
  3. If you need to pass in two parameters, you can use the BiPedicate type to receive Lambda expressions. If you need to pass in multiple parameters, you need to customize an interface.
1 1 0
Share on

Changyi

5 posts | 1 followers

You may also like

Comments

profedsonbelem@gmail.com October 23, 2021 at 8:00 am

parabéns pela postagem

Changyi

5 posts | 1 followers

Related Products