简体   繁体   English

在JAVA中检查对象的空字段

[英]Checking null fields of an object in JAVA

Let's assume I have an object with x amount of fields. 假设我有一个x字段的对象。 Two are allowed to be non-null and the rest has to be null. 允许两个非空,其余的必须为null。 I don't want to do a null check field by field, so I wonder if there is a smart way to do this null check with some of the features of the latest java versions. 我不想逐字段进行空检查,所以我想知道是否有一种聪明的方法来使用最新的java版本的一些功能进行空检查。

I don't want to do a null check field by field 我不想按字段进行空检查

You could avoid writing the check yourself but you necessary need to "mark" the constraints on the fields. 您可以避免自己编写支票,但必须“标记”字段上的约束。
For that you could use the Validator API ( JSR 380 which Hibernate 6 provides the default implementation ) to annotate your class fields with @Null and @NotNull . 为此,您可以使用Validator API( Hibernate 6提供默认实现的 JSR 380 )使用@Null@NotNull注释您的类字段。

And validate the instance explicitly with a Validator. 并使用Validator显式验证实例。
Note that annotated fields could be as mandatory null in a context and not necessarily null in another. 请注意,带注释的字段在上下文中可以是必需的null ,而在另一个上不一定是null And it is compatible with this way as the validator will validate the object only as it is requested : that is on demand. 并且它与这种方式兼容,因为验证器将仅在请求时验证对象:即按需。


According to your comment : 根据你的评论:

I am working on a codebase and your solution is invasive. 我正在开发代码库,您的解决方案是侵入性的。 I would have to get my hands on the low level json parser that creates that pojo. 我必须得到创建该pojo的低级json解析器。 I don't want to do that 我不想那样做

In this case you could use a Map external to the current class that you want to validate. 在这种情况下,您可以使用要验证的当前类的外部Map。
It would allow to maintain the name of the field that you validate and using it in your error message (useful for debugging). 它将允许维护您验证的字段的名称,并在错误消息中使用它(对调试很有用)。

For example : 例如 :

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(","));
}

You can create stream for all the fields in POJO and can check for null 您可以为POJO中的所有字段创建stream ,并可以检查null

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

or 要么

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

If there are only a few fields in the object, and you know it won't change frequently, you could list them as Stream.of arguments as per Deadpool's answer. 如果对象中只有少数字段,并且您知道它不会经常更改,则可以根据Deadpool的答案将它们列为Stream.of参数。 The drawback is violation of the DRY principle: you are repeating the field names: once in the POJO definition and again in the argument list. 缺点是违反DRY原则:您正在重复字段名称:一次在POJO定义中,再次在参数列表中。

If you have many fields (or don't want to repeat yourself) you could use reflection: 如果你有很多字段(或者不想重复自己),你可以使用反射:

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);
        }
    });

Note that use of reflection can have a small performance penalty, likely to be insignificant compared to parsing a JSON string obtained from a web service. 请注意,与解析从Web服务获取的JSON字符串相比,使用反射可能会带来很小的性能损失,这可能是微不足道的。

If you have primitive fields (eg int , boolean , char ) and you want to include them in the checks, restricting them to default values ( 0 , false , '\\0' ) then use the following code: 如果你有原始字段(例如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);
        }
    });

When precisely do you want to do the check? 什么时候你想要检查? The best solution is to create an immutable class and only provide 1 constructor. 最好的解决方案是创建一个不可变类,只提供1个构造函数。 Don't add setters. 不要添加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