繁体   English   中英

在JAVA中检查对象的空字段

[英]Checking null fields of an object in JAVA

假设我有一个x字段的对象。 允许两个非空,其余的必须为null。 我不想逐字段进行空检查,所以我想知道是否有一种聪明的方法来使用最新的java版本的一些功能进行空检查。

我不想按字段进行空检查

您可以避免自己编写支票,但必须“标记”字段上的约束。
为此,您可以使用Validator API( Hibernate 6提供默认实现的 JSR 380 )使用@Null@NotNull注释您的类字段。

并使用Validator显式验证实例。
请注意,带注释的字段在上下文中可以是必需的null ,而在另一个上不一定是null 并且它与这种方式兼容,因为验证器将仅在请求时验证对象:即按需。


根据你的评论:

我正在开发代码库,您的解决方案是侵入性的。 我必须得到创建该pojo的低级json解析器。 我不想那样做

在这种情况下,您可以使用要验证的当前类的外部Map。
它将允许维护您验证的字段的名称,并在错误消息中使用它(对调试很有用)。

例如 :

Foo foo = new Foo();
// set foo fields...

// expected null but was not null
Map<String, Object> hasToBeNullMap = new HashMap<>();
hasToBeNullMap.put("x", foo.getX());
hasToBeNullMap.put("y", foo.getY());
hasToBeNullMap.put("z", foo.getZ());
String errrorMessageForNullExpected = getErrorFieldNames(hasToBeNullMap, Objects::nonNull);

// expected not null but was null
Map<String, Object> hasToBeNotNullMap = new HashMap<>();
hasToBeNotNullMap.put("z", foo.getZ());
String errrorMessageForNotNullExpected = getErrorFieldNames(hasToBeNotNullMap, o -> o == null);

private static String getErrorFieldNames(Map<String, Object> hasToBeNullMap, Predicate<Object> validationPred) {
    return hasToBeNullMap.entrySet()
                         .stream()
                         .filter(validationPred::test)
                         .map(Entry::getKey)
                         .collect(joining(","));
}

您可以为POJO中的所有字段创建stream ,并可以检查null

return Stream.of(id, name).anyMatch(Objects::isNull);

要么

return Stream.of(id, name).allMatch(Objects::isNull);

如果对象中只有少数字段,并且您知道它不会经常更改,则可以根据Deadpool的答案将它们列为Stream.of参数。 缺点是违反DRY原则:您正在重复字段名称:一次在POJO定义中,再次在参数列表中。

如果你有很多字段(或者不想重复自己),你可以使用反射:

boolean valid = Stream.of(YourPojoClass.class.getDeclaredFields())
    .filter(f -> !(f.getName().equals("fieldname allowed to be null") || f.getName.equals("the other field name")))
    .allMatch(f -> {
        f.setAccessible(true);
        try {
            return f.get(o) == null;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    });

请注意,与解析从Web服务获取的JSON字符串相比,使用反射可能会带来很小的性能损失,这可能是微不足道的。

如果你有原始字段(例如intbooleanchar )并且你想将它们包含在支票中,将它们限制为默认值( 0false'\\0' ),那么使用以下代码:

    .allMatch(f -> {
        f.setAccessible(true);
        try {
            return (f.getType() == boolean.class && f.getBoolean(o) == false)
                    || (f.getType().isPrimitive() && f.getDouble(o) == 0)
                    || f.get(o) == null;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    });

什么时候你想要检查? 最好的解决方案是创建一个不可变类,只提供1个构造函数。 不要添加setter。

private String name;
private String nickname;
// Rest of the fields with default (null) values
public Person(String name, String nickname) {
 this.name = name;
 this.nickname = nickname;
}
private Person(){}
// Getters, but no setters

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM