×
Community Blog Coding Specifications | Elegant Java Functions (Part Two)

Coding Specifications | Elegant Java Functions (Part Two)

This article is the second part of the 'Java Coding Specifications' series and aims to impart useful coding advice to Java programmers for enabling them to produce more efficient code.

By Amap_tech team.

Background

This article describes a set of coding rules relevant to Java functions and intends to give some coding advice to Java programmers, which will help them produce more elegant, higher quality, and more efficient code.

Refer to this link to view the first part of this article.

Use the Basic Type for Internal Function Parameters

Case 1) Use the basic type for internal function parameters whenever possible

The following snippet shows the symptom description.

// 调用代码
double price = 5.1D;
int number = 9;
double total = calculate(price, number);

// 计算金额函数
private double calculate(Double price, Integer number) {
    return price * number;
}

Refer to the following recommended solution.

// 调用代码
double price = 5.1D;
int number = 9;
double total = calculate(price, number);

// 计算金额函数
private double calculate(double price, int number) {
    return price * number;
}

Case 2) Use the basic type for the return values of internal functions whenever possible

The following snippet shows the symptom description.

// 获取订单总额函数
public double getOrderAmount(List<Product> productList) {
    double amount = 0.0D;
    for (Product product : productList) {
        if (Objects.isNull(product) || Objects.isNull(product.getPrice())
            || Objects.isNull(product.getNumber())) {
            continue;
        }
        amount += calculate(product.getPrice(), product.getNumber());
    }
    return amount;
}

// 计算金额函数
private Double calculate(double price, double number) {
    return price * number;
}

Refer to the following recommended solution.

// 获取订单总额函数
public double getOrderAmount(List<Product> productList) {
    double amount = 0.0D;
    for (Product product : productList) {
        if (Objects.isNull(product) || Objects.isNull(product.getPrice())
            || Objects.isNull(product.getNumber())) {
            continue;
        }
        amount += calculate(product.getPrice(), product.getNumber());
    }
    return amount;
}

// 计算金额函数
private double calculate(double price, double number) {
    return price * number;
}

This example is for illustration purposes only. It would be better to use stream programming in this case.

Benefits

  • Use the basic type for internal functions whenever possible to avoid packing and unpacking implicit encapsulation types.
  • When the basic type is used for internal function parameters, this syntactically avoids null pointer judgments for these parameters.
  • When the basic type is used for the return values of internal functions, this syntactically avoids null pointer judgments for the return values when the function is called.

Avoid Returning Null Arrays and Lists

Case 1) Avoid the return of null arrays whenever possible to avoid unnecessary null pointer judgments

The following snippet shows the symptom description.

// 调用代码
UserVO[] users = queryUser();
if (Objects.nonNull(users)) {
    for (UserVO user : users) {
        // 处理用户信息
    }
}

// 查询用户函数
private UserVO[] queryUser() {
    // 查询用户列表
    List<UserDO> userList = userDAO.queryAll();
    if (CollectionUtils.isEmpty(userList)) {
        return null;
    }

    // 转化用户数组
    UserVO[] users = new UserVO[userList.size()];
    for (int i = 0; i < userList.size(); i++) {
        UserDO user = userList.get(i);
        users[i] = new UserVO();
        users[i].setId(user.getId());
        users[i].setName(user.getName());
    }

    // 返回用户数组
    return users;
}

Refer to the following recommended solution.

// 调用代码
UserVO[] users = queryUser();
for (UserVO user : users) {
    // 处理用户信息
}

// 查询用户函数
private UserVO[] queryUser() {
    // 查询用户列表
    List<UserDO> userList = userDAO.queryAll();
    if (CollectionUtils.isEmpty(userList)) {
        return new UserVO[0];
    }

    // 转化用户数组
    UserVO[] users = new UserVO[userList.size()];
    for (int i = 0; i < userList.size(); i++) {
        UserDO user = userList.get(i);
        users[i] = new UserVO();
        users[i].setId(user.getId());
        users[i].setName(user.getName());
    }

    // 返回用户数组
    return users;
}

Case 2) Avoid the return of null lists whenever possible to avoid unnecessary null pointer judgments

The following snippet shows the symptom description.

// 调用代码
List<UserVO> userList = queryUser();
if (Objects.nonNull(userList)) {
    for (UserVO user : userList) {
        // 处理用户信息
    }
}

// 查询用户函数
private List<UserVO> queryUser(){
    // 查询用户列表
    List<UserDO> userList = userDAO.queryAll();
    if(CollectionUtils.isEmpty(userList)) {
        return null;
    }

    // 转化用户列表
    List<UserVO> userVoList = new ArrayList<>(userList.size());
    for(UserDO user : userList) {
        UserVO userVo = new UserVO();
        userVo.setId(user.getId());
        userVo.setName(user.getName());
        userVoList.add(userVo);
    }

    // 返回用户列表
    return userVoList;
}

Refer to the following recommended solution.

// 调用代码
List<UserVO> userList = queryUser();
for (UserVO user : userList) {
   // 处理用户信息
 }

// 查询用户函数
private List<UserVO> queryUser(){
    // 查询用户列表
    List<UserDO> userList = userDAO.queryAll();
    if(CollectionUtils.isEmpty(userList)) {
        return Collections.emptyList();
    }

    // 转化用户列表
    List<UserVO> userVoList = new ArrayList<>(userList.size());
    for(UserDO user : userList) {
        UserVO userVo = new UserVO();
        userVo.setId(user.getId());
        userVo.setName(user.getName());
        userVoList.add(userVo);
    }

    // 返回用户列表
    return userVoList;
}

Benefits

  • Make sure that the returned arrays and lists are not null to avoid null pointer judgments.

Encapsulate Function Input Parameters

Case 1) When too many parameters are input, they should be encapsulated as parameter classes

Java specifications disallow too many function parameters, which makes it difficult to maintain and expand functions.

The following snippet shows the symptom description.

// 修改用户函数
public void modifyUser(Long id, String name, String phone, Integer age, 
    Integer sex, String address, String description) {
    // 具体实现逻辑
}

Refer to the following recommended solution.

// 修改用户函数
public void modifyUser(User user) {
    // 具体实现内容
}

// 用户类
@Getter
@Setter
@ToString
private class User{
    private Long id;
    private String name;
    private String phone;
    private Integer age;
    private Integer sex;
    private String address;
    private String description;
}

Encapsulate Group Parameters as a Parameter Class

Since parameters appear in groups, it is necessary to encapsulate a class to describe this situation.

The following snippet shows the symptom description.

// 获取距离函数
public double getDistance(double x1, double y1, double x2, double y2) {
    // 具体实现逻辑
}

Refer to the following recommended solution.

// 获取距离函数
public double getDistance(Point point1, Point point2) {
    // 具体实现逻辑
}

// 点类
@Getter
@Setter
@ToString
private class Point{
    private double x;
    private double y;
}

Benefits

  • Encapsulate multiple function parameters as classes to make functions easier to expand and maintain.
  • Encapsulate group function parameters as classes to clarify the business.

Use Functions to Implement Anonymous Internal Classes Whenever Possible

Advantages and disadvantages of anonymous internal classes in Java: In an anonymous internal class (including Lambda expressions), you may directly access the members of external classes, including the member variables of classes and the internal variables of functions. In this case, since you access external variables at will, the boundaries of the code are unclear.

We recommend using Lambda expressions to simplify anonymous internal classes and then employing functions to implement complex Lambda expressions.

Case 1) Use functions to implement anonymous internal classes (including Lambda expressions) whenever possible

The following snippet shows the symptom description.

// 发送结算数据
sendWorkerSettleData(WorkerPushDataType.CHECKER, () -> {
    Date beginDate = DateUtils.addDays(currDate, -aheadDays);
    Date endDate = DateUtils.addDays(currDate, 1);
    return auditTaskDAO.statCheckerSettleData(beginDate, endDate);
});

Refer to the following recommended solution.

// 发送结算数据
sendWorkerSettleData(WorkerPushDataType.CHECKER, () -> statCheckerSettleData(currDate, aheadDays));

// 统计验收员结算数据函数
private List<WorkerSettleData> statCheckerSettleData(Date currDate, int aheadDays) {
    Date beginDate = DateUtils.addDays(currDate, -aheadDays);
    Date endDate = DateUtils.addDays(currDate, 1);
    return auditTaskDAO.statCheckerSettleData(beginDate, endDate);
}

In fact, there is a simpler way. To calculate the start date and end date before calling the sendWorkerSettleData function (to send data to the operator for settlement), use the function auditTaskDAO.statCheckerSettleData (beginDate, endDate) instead of the anonymous internal class.

Case 2) Split complex anonymous internal class implementation APIs into multiple function class APIs

If the functions of the API implemented by an anonymous internal class are less relevant to each other, split the API into several functional APIs to facilitate the use of Lambda expressions.

The following snippet shows the symptom description.

// 清除过期数据
cleanExpiredData("用户日志表", new CleanExpiredDataOperator() {
    @Override
    public List<Date> queryExpiredDate(Integer remainDays) {
        return userDAO.queryExpiredDate(remainDays);
    }
    @Override
    public void cleanExpiredData(Date expiredDate) {
        userDAO.cleanExpiredData(expiredDate);
    }
});

// 清除过期数据函数
private void cleanExpiredData(String tableName, CleanExpiredDataOperator ,
cleanExpiredDataOperator) {
    // 功能实现代码
}

// 清除过期操作接口
interface CleanExpiredDataOperator {
    // 查询过期日期
    public List<Date> queryExpiredDate(Integer remainDays);
    // 清除过期数据
    public void cleanExpiredData(Date expiredDate);
}

Refer to the following recommended solution.

// 清除过期数据
cleanExpiredData("用户日志表", userDAO::queryExpiredDate,userDAO::cleanExpiredData);

// 清除过期数据函数
private void cleanExpiredData(String tableName, QueryExpiredDateOperator queryExpiredDateOperator, CleanExpiredDataOperator cleanExpiredDataOperator) {
    // 功能实现代码
}

// 查询过期日期接口
interface QueryExpiredDateOperator {
    // 查询过期日期
    public List<Date> queryExpiredDate(Integer remainDays);
}

// 清除过期操作接口
interface CleanExpiredDataOperator {
    // 清除过期数据
    public void cleanExpiredData(Date expiredDate);
}

Benefits

  • Define functions and specify parameters to clarify the code boundaries of anonymous internal classes.
  • Use Lambda expressions to simplify the implementation of anonymous internal classes and streamline the code.

Use return to Remove Unnecessary Code

Case 1) Delete unnecessary "if's"

The following snippet shows the symptom description.

// 是否通过函数
public boolean isPassed(Double passRate) {
    if (Objects.nonNull(passRate) && passRate.compareTo(PASS_THRESHOLD) >= 0) {
        return true;
    }
    return false;
}

Refer to the following recommended solution.

// 是否通过函数
public boolean isPassed(Double passRate) {
    return Objects.nonNull(passRate) && passRate.compareTo(PASS_THRESHOLD) >= 0;
}

Case 2) Delete unnecessary "else's"

The following snippet shows the symptom description.

// 结算工资函数
public double settleSalary(Long workId, int workDays) {
    // 根据是否合格处理
    if (isQualified(workId)) {
        return settleQualifiedSalary(workDays);
    } else {
        return settleUnqualifiedSalary(workDays);
    }
}

Refer to the following recommended solution.

// 结算工资函数
public double settleSalary(Long workId, int workDays) {
    // 根据是否合格处理
    if (isQualified(workId)) {
        return settleQualifiedSalary(workDays);
    }
    return settleUnqualifiedSalary(workDays);
}

Case 3) Delete unnecessary variables

The following snippet shows the symptom description.

// 查询用户函数
public List<UserDO> queryUser(Long id, String name) {
    UserQuery userQuery = new UserQuery();
    userQuery.setId(id);
    userQuery.setName(name);
    List<UserDO> userList = userDAO.query(userQuery);
    return userList;
}

Refer to the following recommended solution.

// 查询用户函数
public List<UserDO> queryUser(Long id, String name) {
    UserQuery userQuery = new UserQuery();
    userQuery.setId(id);
    userQuery.setName(name);
    return userDAO.query(userQuery);
}

Benefits

  • Delete unnecessary code lines for more streamlined and convenient code.

Use Temporary Variables to Optimize the Code

In some code segments, you often see the a.getB().getC()...getN() syntax. This is referred to as a cascaded function call and results in poor code robustness and readability. We recommend that you should not perform cascaded function calls. Instead, use temporary variables to split the calls and perform null pointer checks on objects.

Case 1) Use temporary variables to clarify the logic

The following snippet shows the symptom description.

// 是否土豪用户函数
private boolean isRichUser(User user) {
    return Objects.nonNull(user.getAccount())
        && Objects.nonNull(user.getAccount().getBalance())
        && user.getAccount().getBalance().compareTo(RICH_THRESHOLD) >= 0;
}

This is an easy method for simplified code control, but it results in poor readability.

Refer to the following recommended solution.

// 是否土豪用户函数
private boolean isRichUser(User user) {
    // 获取用户账户
    UserAccount account = user.getAccount();
    if (Objects.isNull(account)) {
        return false;
    }

    // 获取用户余额
    Double balance = account.getBalance();
    if (Objects.isNull(balance)) {
        return false;
    }

    // 比较用户余额
    return balance.compareTo(RICH_THRESHOLD) >= 0;
}

This solution increases code lines but clarifies the logic. When you find that it is difficult to balance simplicity and readability, we recommend weighing the readability more.

Case 2) Use temporary variables to simplify the code

The following snippet shows the symptom description.

// 构建用户函数
public UserVO buildUser(UserDO user) {
    UserVO vo = new UserVO();
    vo.setId(user.getId());
    vo.setName(user.getName());
    if (Objects.nonNull(user.getAccount())) {
        vo.setBalance(user.getAccount().getBalance());
        vo.setDebt(user.getAccount().getDebt());
    }
    return vo;
}

The code is written this way to reduce temporary variables.

Refer to the following recommended solution.

// 构建用户函数
public UserVO buildUser1(UserDO user) {
    UserVO vo = new UserVO();
    vo.setId(user.getId());
    vo.setName(user.getName());
    UserAccount account = user.getAccount();
    if (Objects.nonNull(account)) {
        vo.setBalance(account.getBalance());
        vo.setDebt(account.getDebt());
    }
    return vo;
}

Benefits

  • Use temporary variables to clarify the business logic.
  • Use temporary variables to simplify the code. The variable name indicates its purpose, which avoids lots of useless code.
  • If retrieving functions is complex and time-consuming, use temporary variables to improve the running efficiency.
  • Use temporary variables to avoid cascaded function calls and prevent null pointer exceptions.

Only Retain Parameters Required by the Function

In some code segments, you often see the a.getB().getC()...getN() syntax. This is referred to as a cascaded function call and results in poor code robustness and readability. We recommend that you not perform cascaded function calls. Instead, use temporary variables to split the calls and perform null pointer checks on objects.

Case 1) Delete redundant parameters

The following snippet shows the symptom description.

// 修改用户状态函数
private void modifyUserStatus(Long userId, Integer status, String unused) {
    userCache.modifyStatus(userId, status);
    userDAO.modifyStatus(userId, status);
}
其中,unused参数是无用参数。

建议方案:

// 修改用户状态函数
private void modifyUserStatus(Long userId, Integer status) {
    userCache.modifyStatus(userId, status);
    userDAO.modifyStatus(userId, status);
}

Case 2) Replace objects with attributes

The following snippet shows the symptom description.

// 删除用户函数
private void deleteUser(User user) {
    userCache.delete(user.getId());
    userDAO.delete(user.getId());
}

Refer to the following recommended solution.

// 删除用户函数
private void deleteUser(Long userId) {
    userCache.delete(userId);
    userDAO.delete(userId);
}

When calling a function, there's no need to build a dedicated parameter object. When a function uses more than three attributes, you do not need to apply this rule.

Benefits

Retain only the parameters required by the function to clarify the parameters that need to be assigned values during the call and avoid constructing useless parameters during the call.

Postscript

If you have better suggestions or better code cases, we welcome your input. We hope that this article serves as a reference and contribute to the formation of a complete set of Java coding specifications.

0 0 0
Share on

Alibaba Clouder

2,605 posts | 747 followers

You may also like

Comments