问题列表
空指针异常
NPE 或许是编程语言中最常见的问题,被 Null 的发明者托尼·霍尔(Tony Hoare)称之为十亿美元的错误。在 Java 中并没有内置的处理 Null 值的语法,但仍然存在一些相对优雅的方式能够帮助我们的规避 NPE。
-
使用 JSR-305/jetbrain 等注解
-
NotNull
-
Nullable

通过在方法参数、返回值、字段等位置显式标记值是否可能为 Null,配合代码检查工具,能够在编码阶段规避绝大部分的 NPE 问题,建议至少在常用方法或者对外 API 中使用该注解,能够对调用方提供显著的帮助。
-
用 Optional 处理链式调用
Optional 源于 Guava 中的 Optional 类,后 Java 8 内置到 JDK 中。Optional 一般作为函数的返回值,强制提醒调用者返回值可能不存在,并且能够通过链式调用优雅的处理空值。
public class OptionalExample {public static void main(String[] args) {// 使用传统空值处理方式User user = getUser();String city = "DEFAULT";if (user != null && user.isValid()) {Address address = user.getAddress();if (adress != null) {city = adress.getCity();}}System.out.println(city);// 使用 Optional 的方式Optional<User> optional = getUserOptional();city = optional.filter(User::isValid).map(User::getAddress).map(Adress::getCity).orElse("DEFAULT")System.out.println(city);}@Nullablepublic static User getUser() {return null;}public static Optional<User> getUserOptional() {return Optional.empty();}@Datapublic static class User {private Adress address;private boolean valid;}@Datapublic static class Address {private String city;}}
-
用 Objects.equals(a,b) 代替 a.equals(b)
equals方法是 NPE 的高发地点,用 Objects.euqals来比较两个对象,能够避免任意对象为 null 时的 NPE。
-
使用空对象模式
空对像模式通过一个特殊对象代替不存在的情况,代表对象不存在时的默认行为模式。常见例子:
用 Empty List 代替 null,EmptyList 能够正常遍历:
public class EmptyListExample {public static void main(String[] args) {List<String> listNullable = getListNullable();if (listNullable != null) {for (String s : listNullable) {System.out.println(s);}}List<String> listNotNull = getListNotNull();for (String s : listNotNull) {System.out.println(s);}}@Nullablepublic static List<String> getListNullable() {return null;}@NotNullpublic static List<String> getListNotNull() {return Collections.emptyList();}}
空策略
public class NullStrategyExample {private static final Map<String, Strategy> strategyMap = new HashMap<>();public static void handle(String strategy, String content) {findStrategy(strategy).handle(content);}@NotNullprivate static Strategy findStrategy(String strategyKey) {return strategyMap.getOrDefault(strategyKey, new DoNothing());}public interface Strategy {void handle(String s);}// 当找不到对应策略时, 什么也不做public static class DoNothing implements Strategy {@Overridepublic void handle(String s) {}}}
对象转化
在业务应用中,我们的代码结构往往是多层次的,不同层次之间经常涉及到对象的转化,虽然很简单,但实际上繁琐且容易出错。
反例 1:
public class UserConverter {public static UserDTO toDTO(UserDO userDO) {UserDTO userDTO = new UserDTO();userDTO.setAge(userDO.getAge());// 问题 1: 自己赋值给自己userDTO.setName(userDTO.getName());return userDTO;}@Datapublic static class UserDO {private String name;private Integer age;// 问题 2: 新增字段未赋值private String address;}@Datapublic static class UserDTO {private String name;private Integer age;}}
反例2:
public class UserBeanCopyConvert {public UserDTO toDTO(UserDO userDO) {UserDTO userDTO = new UserDTO();// 用反射复制不同类型对象.// 1. 重构不友好, 当我要删除或修改 UserDO 的字段时, 无法得知该字段是否通过反射被其他字段依赖BeanUtils.copyProperties(userDO, userDTO);return userDTO;}}
-
使用 Mapstruct
Mapstruct 使用编译期代码生成技术,根据注解, 入参,出参自动生成转化,代码,并且支持各种高级特性,比如:
-
未映射字段的处理策略,在编译期发现映射问题;
-
复用工具,方便字段类型转化;
-
生成 spring Component 注解,通过 spring 管理;
-
等等其他特性;
@Mapper(componentModel = "spring",unmappedSourcePolicy = ReportingPolicy.ERROR,unmappedTargetPolicy = ReportingPolicy.ERR

最低0.47元/天 解锁文章
939

被折叠的 条评论
为什么被折叠?



