Reputation: 99
Let's assume I have an object with x amount of fields. Two are allowed to be non-null and the rest has to be 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.
Upvotes: 4
Views: 9128
Reputation: 6123
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. 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.
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.
If you have primitive fields (e.g. 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:
.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);
}
});
Upvotes: 1
Reputation: 131346
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
.
And validate the instance explicitly with a Validator.
Note that annotated fields could be as mandatory null
in a context and not necessarily null
in another. 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. 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.
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(","));
}
Upvotes: 1
Reputation: 10562
When precisely do you want to do the check? The best solution is to create an immutable class and only provide 1 constructor. Don't add setters.
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
Upvotes: 0
Reputation: 40048
You can create stream
for all the fields in POJO and can check for null
return Stream.of(id, name).anyMatch(Objects::isNull);
or
return Stream.of(id, name).allMatch(Objects::isNull);
Upvotes: 2