(二)Java使用奇技淫巧之枚举+自定义异常+接口优雅实现判断参数为空

发布时间:2023-04-04 17:30

目录

1.背景

2.枚举实现接口

2.1 接口实现默认方法

2.2 枚举类实现接口

3.定义枚举接口判断参数

3.1 自定义异常类

3.2 定义判断参数接口

3.3 定义参数枚举类

4.实际使用演示


1.背景

平时开发的时候判断某个接口参数是否为空是实现接口健壮性最基础性的操作,可以确保在接下来的流程中哪些参数是可靠的,哪些参数是不可靠的以方便开发人员写出更简洁和安全的代码。假设判断参数的地方不统一这里一块那里一块将会导致开发人员后续维护或者开发的时候不容易判断必传参数情况。

比如以下场景:

从Controller接收进来请求:

@RestController
@RequestMapping(\"/test\")
public class TestController {
    @Autowired
    private TestService testService;
    @PostMapping(\"/test\")
    public Object test(@RequestBody Map request) {
        // 进行必要参数校验
        if (request.get(\"test1\")) == null || request.get(\"test2\") == null 
                || request.get(\"test3\") == null) {
            throw new RuntimeException(\"参数异常\");
        }
        if (reqeust.get(\"test4\") == null) {
            throw new RuntimeException(\"test3 参数为空\");
        }
        List paramInfo = (List) request.get(\"testList1\");
        testService.test(paramInfo);
        ...
    }
}

在Controller层就进行了一系列的参数判断,接着进入到了Service中:

@Service
public class TestService {
    public void test(String[] paramInfo) {
        if (!paramInfo.contains(\"test6\")) {
            throw new RuntimeException(\"test6参数为空\");
        }
        ...
    }
}

针对这种的情况在此提供一种思路用来可以统一管理接口必传参数以及简化判断的语句。

2.枚举实现接口

虽然枚举类不能继承某个类,但是可以实现某个接口,且JDK8可以在接口中使用default关键词来实现接口中的声明方法,利用Java的这些特性我们便可以实现类似一个公共父类的情景,定义一个接口,再实现某些接口方法即可。

2.1 接口实现默认方法

可以实现接口声明的默认方法,如下代码所示:

public interface TestCheck {
    /**
     * 检查方法
     */
    default void check() {
        // 在此可以实现默认方法逻辑
        System.out.println(\"接口默认实现方法\");
    }
}

2.2 枚举类实现接口

枚举可以实现接口并被外调用时接口方法也可以直接被调用,如下代码所示:

public enum TestEnums implements TestCheck {
    TEST1;
}

其它的方法可以直接调用接口方法:

public class MainClazz {
    public static void main(String[] args) {
        TestEnums.TEST1.check();
    }
}
-----------打印结果
接口默认实现方法

如此一来我们便可以使用这种组合方式来完成更加便利的操作了。

3.定义枚举接口判断参数

3.1 自定义异常类

首先先定义一个异常类,这个自定义异常类继承自RuntimeException,因为直接继承Excecption抛出的时候会要求捕获,而RuntimeException则不需要。

public class BusinessException extends RuntimeException {
    private static final String BUSINESS_EXCEPTION_MESSAGE = 
            \"CustomException\";
    
    public BusinessException() {
        super(BUSINESS_EXCEPTION_MESSAGE);
    }
    
    public BusinessException(String message) {
        super(message);
    }
}

3.2 定义判断参数接口

需要定义获取参数的方法和处理具体参数判断的默认实现方法:

public interface BaseParamCheck {
    /**
     * 定义获取参数的方法
     *
     * @return
     */
    String[] getParam();
    
    /**
     * 检测数组参数是否存在
     *
     * @param params
     */
    default void check(Collection params) {
        String[] ownParam = getParam().clone();
        if (ownParam == null || ownParam.length == 0) {
            return ;
        }
        if (params == null || params.length == 0) {
            throwError(\"string[] params\");
        }
        for (String param : ownParam) {
            if (Arrays.binarySearch(params, param, String::compareTo) 
                    != params.length) {
                throwError(param);
            }
        }
    }
    
    /**
     * 抛出异常
     */
    default void throwError(String param) {
        throw new BusinessException(param + \"参数为空\");
    }
    
    /**
     * 检测数组参数是否存在
     *
     * @param params
     */
    default void check(List params) {
        check((Collection) params);
    }

    /**
     * 检测map集合中参数是否存在
     *
     * @param params
     */
    default void check(Map params) {
        if (CollectionUtils.isEmpty(params)) {
            throwError(\"map params\");
        }
        check(params.keySet());
    }
}

有了可以在接口中实现默认方法的default关键词后,接口将可以变得更加的灵活,比如代码中的getParam()完全可以设置成更丰富的对象,而throwError()方法中也可以使用更丰富的逻辑,比如使用本系统的特殊Exception对象等。当然也可以扩展支持检查类型甚至返回参数。

3.3 定义参数枚举类

定义实现刚刚接口的枚举类:

public enum ParamCheck implements BaseParamCheck {
    /**
     * 判断controller中的test方法
     */
    CONTROLLER_TEST(\"test1\", \"test2\", \"test3\", \"test4\"),
    /**
     * 判断service中的test方法
     */
    SERVICE_TEST(\"test6\");
    
    private String[] param;
    
    ParamCheck(String ... param) {
        this.param = param;
    }
    
    @Override
    public String[] getParam() {
        return this.param;
    }
}

仅需要实现接口留给枚举的特定方法,如本示例中仅留了一个getParam()方法以方便枚举类定义某种情况需要检验的必传参数。

4.实际使用演示

还是以最开始的例子来展示,使用枚举加接口的实现方式后必要参数校验就变成了如下所示的样子:

从Controller接收进来请求:

@RestController
@RequestMapping(\"/test\")
public class TestController {
    @Autowired
    private TestService testService;
    @PostMapping(\"/test\")
    public Object test(@RequestBody Map request) {
        // 参数校验,简洁明了
        ParamCheck.CONTROLLER_TEST.check(request);
        List paramInfo = (List) request.get(\"testList1\");
        testService.test(paramInfo);
        ...
    }
}

在Controller层就进行了一系列的参数判断,接着进入到了Service中:

@Service
public class TestService {
    public void test(String[] paramInfo) {
        ParamCheck.SERVICE_TEST.check(request);
        ...
    }
}

经过这种配置后如果想要了解某个方法的必传参数则直接去枚举中看下有哪些参数即可,使用时也直接可以使用一个语句解决,如果要使用更加复杂的判断逻辑则在接口中接收lambda语法函数即可。

使用这种方式的唯一好处便是方便管理各种情景下的必传参数校验,提升使用时的便利性,并且这样做也很方便和系统的错误码统一处理相结合,只需要将异常对象改成可以处理的类型即可。

如果看过上一篇的枚举+自定义异常+断言减少if-throw异常抛出文章可以知道系统中会有个ErrorCode枚举类,这样的话就可以直接用ErrorCode中的枚举属性来代表具体的错误码类型,使用起来十分方便。

上一篇的链接:(一)Java使用奇技淫巧之枚举+自定义异常+断言减少if-throw异常抛出

 

 

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号