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.
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);
}
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:
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.
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:
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;
...
}
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>
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
...
});
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.
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;
}
}
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.
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.
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.
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;
}
}
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;
}
}
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.
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.
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.
/** 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;
}
}
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.
JavaBean is a Java class that follows a specific writing method. It usually has the following characteristics:
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:
Disadvantages:
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:
Disadvantages:
/** 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:
Disadvantages:
Collection constants such as list constants, set constants, and map constants are often used in coding.
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;
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(); // []
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);
}
...
}
The previous section describes how to define set constants, and this section describes how to define 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]
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.
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]
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.
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.
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?
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:
Alibaba Cloud Community - April 24, 2023
Alibaba Clouder - May 3, 2018
Alibaba Clouder - June 13, 2018
Alibaba Cloud Community - September 13, 2021
Nick Patrocky - August 26, 2022
Alibaba Cloud Community - December 4, 2024
Explore Web Hosting solutions that can power your personal website or empower your online business.
Learn MoreA low-code development platform to make work easier
Learn MoreExplore how our Web Hosting solutions help small and medium sized companies power their websites and online businesses.
Learn MoreHelp enterprises build high-quality, stable mobile apps
Learn More
profedsonbelem@gmail.com October 23, 2021 at 8:00 am
parabéns pela postagem