Backend Development 14 min read

Using Java 8 Functional Interfaces to Refactor Data Validation and Reduce Duplicate Code

This article demonstrates how Java 8’s functional interfaces, such as Function and SFunction, can be leveraged to refactor repetitive data validation logic, introducing generic methods like ensureColumnValueValid and validateColumnValueMatchesExpected that improve code reuse, readability, and maintainability in backend services.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Using Java 8 Functional Interfaces to Refactor Data Validation and Reduce Duplicate Code

Introduction

In Java development we often encounter repetitive code that makes projects bulky and raises maintenance costs. Java 8 brings functional programming features, especially the Function interface, which provide a powerful way to eliminate such duplication.

Background: Data Validation Pain Points

Typical business systems require frequent validation of database fields (e.g., user ID, department ID). Traditional implementations repeat similar query logic for each entity, leading to verbose and hard‑to‑maintain code.

// 判断用户 ID 是否有效
public void checkUserExistence(String userId) {
    User user = userDao.findById(userId);
    if (user == null) {
        throw new RuntimeException('用户ID无效');
    }
}

 // 判断部门 ID 是否有效
public void checkDeptExistence(String deptId) {
    Dept dept = deptDao.findById(deptId);
    if (dept == null) {
        throw new RuntimeException('部门ID无效');
    }
}

Java 8 Functional Interfaces

Java 8 introduces functional interfaces; the basic Function<T,R> accepts a T and returns an R. In frameworks like MyBatis‑Plus, SFunction further wraps lambda expressions, allowing flexible manipulation of entity properties.

Practical Refactor: Generic Assertion Method

The method ensureColumnValueValid uses functional interfaces to assert the validity of any entity column value:

/**
 * 确认数据库字段值有效(通用)
 * @param
待验证值的类型
 * @param valueToCheck 待验证的值
 * @param columnExtractor 实体类属性提取函数
 * @param queryExecutor 单条数据查询执行器
 * @param errorMessage 异常提示信息模板
 */
public static
void ensureColumnValueValid(
        V valueToCheck,
        SFunction
columnExtractor,
        SFunction
, T> queryExecutor,
        String errorMessage) {
    if (valueToCheck == null) return;
    LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>();
    wrapper.select(columnExtractor);
    wrapper.eq(columnExtractor, valueToCheck);
    wrapper.last('LIMIT 1');
    T entity = queryExecutor.apply(wrapper);
    R columnValue = columnExtractor.apply(entity);
    if (entity == null || columnValue == null) {
        throw new DataValidationException(String.format(errorMessage, valueToCheck));
    }
}

This method receives the value to check, a column extractor, a query executor, and an error‑message template, enabling concise and reusable validation logic.

Comparison

Before Refactor

public void checkUserExistence(String userId) {
    User user = userDao.findById(userId);
    if (user == null) {
        throw new RuntimeException('用户ID无效');
    }
}

public void checkDeptExistence(String deptId) {
    Dept dept = deptDao.findById(deptId);
    if (dept == null) {
        throw new RuntimeException('部门ID无效');
    }
}

After Refactor

public void assignTaskToUser(AddOrderDTO dto) {
    ensureColumnValueValid(dto.getUserId(), User::getId, userDao::getOne, '用户ID无效');
    ensureColumnValueValid(dto.getDeptId(), Dept::getId, deptDao::getOne, '部门ID无效');
    ensureColumnValueValid(dto.getCustomerId(), Customer::getId, customerDao::getOne, '客户ID无效');
    // ... other business logic
}

The refactored version dramatically reduces code volume and makes the intent clearer.

Advantages

Reduced duplicate code: ensureColumnValueValid centralises validation logic.

Enhanced reuse: The generic method works for any entity attribute.

Improved readability and maintainability: Clear signatures and lambda expressions express intent directly.

Flexibility and extensibility: Updating validation rules only requires changes inside ensureColumnValueValid .

Extending Validation Logic

Assert Column Equals Expected Value

Method validateColumnValueMatchesExpected verifies that a queried column matches an expected value:

public static
void validateColumnValueMatchesExpected(
        SFunction
targetColumn, R expectedValue,
        SFunction
conditionColumn, C conditionValue,
        SFunction
, T> queryMethod,
        String errorMessage) {
    LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>();
    wrapper.select(targetColumn);
    wrapper.eq(conditionColumn, conditionValue);
    T one = queryMethod.apply(wrapper);
    if (one == null) return;
    R actualValue = targetColumn.apply(one);
    if (!Objects.equals(actualValue, expectedValue)) {
        throw new RuntimeException(String.format(errorMessage, expectedValue, actualValue));
    }
}

Example usage in a permission‑management scenario:

validateColumnValueMatchesExpected(User::getRoleType, '普通用户', User::getId, userId, userMapper::getOne, '用户角色不是普通用户,无法升级为管理员!');

Assert Value Within Expected List

Method validateColumnValueInExpectedList checks that a column value belongs to a predefined list:

public static
void validateColumnValueInExpectedList(
        SFunction
targetColumn, List
expectedValueList,
        SFunction
conditionColumn, C conditionValue,
        SFunction
, T> queryMethod,
        String errorMessage) {
    LambdaQueryWrapper
wrapper = new LambdaQueryWrapper<>();
    wrapper.select(targetColumn);
    wrapper.eq(conditionColumn, conditionValue);
    T one = queryMethod.apply(wrapper);
    if (one == null) return;
    R actualValue = targetColumn.apply(one);
    if (actualValue == null) {
        throw new RuntimeException('列查询结果为空');
    }
    if (!expectedValueList.contains(actualValue)) {
        throw new RuntimeException(errorMessage);
    }
}

Example in an e‑commerce order‑cancellation flow:

List
cancelableStatuses = Arrays.asList(OrderStatusEnum.WAITING_PAYMENT.getValue(), OrderStatusEnum.WAITING_DELIVERY.getValue());
validateColumnValueInExpectedList(Order::getStatus, cancelableStatuses, Order::getOrderId, orderId, orderMapper::selectOne, '订单当前状态不允许取消!');

Core Advantages

Code reuse: Generic, functional‑interface‑based methods adapt to any entity and attribute.

Clear intent: Method signatures convey validation purpose directly.

Flexibility: Simple lambda arguments configure complex query logic without exposing low‑level details.

Maintainability: Adding new validation scenarios only requires calling the existing generic methods; rule changes are centralized.

The Power of Functional Programming

This example shows how functional programming in Java 8 can dramatically simplify code, increase abstraction, and improve correctness and testability, making it a valuable paradigm for everyday backend development.

JavaBackend DevelopmentlambdaFunctional Programmingdata validation
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.