How Java 8 Functional Interfaces Can Eliminate Repetitive Validation Code
This article demonstrates how Java 8’s functional interfaces, especially Function and SFunction, can refactor repetitive data‑validation logic into a generic, reusable method, reducing boilerplate, improving readability, and enabling flexible validation scenarios such as checking column values, matching expected values, and verifying values against allowed lists.
Problem
In Java projects, validation of database column values often leads to duplicated boilerplate code such as separate methods for checking user IDs, department IDs, etc. This duplication makes the codebase bulky and harder to maintain.
Functional Interface Approach
Java 8 introduced functional interfaces like Function<T,R>. In MyBatis‑Plus the SFunction interface wraps lambda expressions to provide type‑safe property extraction. Combined with LambdaQueryWrapper, it enables concise, reusable query construction.
Generic Validation Method
/**
* Validate that a column value exists in the database.
*
* @param <V> Type of the value to check
* @param valueToCheck The value to validate (may be null)
* @param columnExtractor Function extracting the target column from the entity
* @param queryExecutor Function that executes a single‑row query using a wrapper
* @param errorMessage Exception message template, e.g. "User ID %s is invalid"
*/
public static <T, R, V> void ensureColumnValueValid(
V valueToCheck,
SFunction<T, R> columnExtractor,
SFunction<LambdaQueryWrapper<T>, T> queryExecutor,
String errorMessage) {
if (valueToCheck == null) return;
LambdaQueryWrapper<T> 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));
}
}Usage Example
public void assignTaskToUser(AddOrderDTO dto) {
ensureColumnValueValid(dto.getUserId(), User::getId, userDao::getOne, "User ID %s is invalid");
ensureColumnValueValid(dto.getDeptId(), Dept::getId, deptDao::getOne, "Dept ID %s is invalid");
ensureColumnValueValid(dto.getCustomerId(), Customer::getId, customerDao::getOne, "Customer ID %s is invalid");
// ... other business logic
}Advantages
Reduced duplicate code : One generic method handles all column‑value checks.
Improved reuse : Works for any entity and any property.
Clear intent : Method signature expresses validation purpose.
Flexibility : Changing validation rules only requires updating the generic method.
Validate Column Equals Expected Value
/**
* Verify that a column value matches an expected value.
*/
public static <T, R, C> void validateColumnValueMatchesExpected(
SFunction<T, R> targetColumn, R expectedValue,
SFunction<T, C> conditionColumn, C conditionValue,
SFunction<LambdaQueryWrapper<T>, T> queryMethod,
String errorMessage) {
LambdaQueryWrapper<T> 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 – ensure a user’s role is "普通用户" before promotion:
validateColumnValueMatchesExpected(
User::getRoleType, "普通用户",
User::getId, userId,
userMapper::getOne,
"User role is not 普通用户, cannot promote to admin");Validate Column Value Within an Allowed List
/**
* Verify that a column value is contained in a predefined list.
*/
public static <T, R, C> void validateColumnValueInExpectedList(
SFunction<T, R> targetColumn, List<R> expectedValueList,
SFunction<T, C> conditionColumn, C conditionValue,
SFunction<LambdaQueryWrapper<T>, T> queryMethod,
String errorMessage) {
LambdaQueryWrapper<T> 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("Query result is null");
}
if (!expectedValueList.contains(actualValue)) {
throw new RuntimeException(errorMessage);
}
}Example – allow order cancellation only when the order status is in a cancelable list:
List<String> cancelable = Arrays.asList(
OrderStatusEnum.WAITING_PAYMENT.getValue(),
OrderStatusEnum.WAITING_DELIVERY.getValue()
);
validateColumnValueInExpectedList(
Order::getStatus, cancelable,
Order::getOrderId, orderId,
orderMapper::selectOne,
"Current order status does not allow cancellation");Core Strengths
Code reuse : Generic method adapts to any entity and attribute.
Expressive intent : Signature clearly describes validation logic.
Flexibility : Simple lambda expressions configure complex queries without touching low‑level details.
Maintainability : Adding new validations only requires invoking the generic method; rule changes are centralized.
Conclusion
By leveraging Java 8 functional programming and MyBatis‑Plus SFunction, developers can replace repetitive validation code with a small set of reusable, type‑safe utilities. This leads to more concise, maintainable, and testable data‑validation logic.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
