Suppose I have an Excel like data row class DataRow
and I would like to compare two rows and call an function to update one cell in a row if these two rows are different. My set up is currently this, using reflection to get each column's value (field):
try {
Field[] fields = oldRow.getClass().getDeclaredFields();
Arrays.stream(fields)
.forEach(field -> field.setAccessible(true))
.filter(field -> !field.get(oldRow).equals(field.get(newRow))
.findAny()
.ifPresent(newRow.updateACell())
.orElse(newRow.udpateACell("new value"))
} catch (Exception e){
System.out.println(e);
}
However, this code will give me an error because ifPresent
does not allow 'void' type here. I understand the function should be invoked upon the value ifPresent
receives, but is there anyway to achieve what I want to without using if else statements or for loop?
The base of the code is not compilable. Stream#forEach
returns void
, therefore you cannot perform Stream#filter
on that. Use either Stream#peek
(please, read this) or Stream#map
.
Arrays.stream(fields)
.peek(field -> field.setAccessible(true))
.filter(field -> !field.get(oldRow).equals(field.get(newRow))
...
Better use an appropriate method to avoid Stream#map
/Stream#peek
as of Java 9 (big thanks to @Slaw's comment):
Arrays.stream(fields)
.filter(field -> field.trySetAccessible() && !rowsAreEqual(field, oldRow, newRow))
...
The method Field#get
throws an exception that must be handled. The Stream API is not suitable for it, but you can create a wrapper to avoid the boilerplate.
private static boolean rowsAreEqual(Field field, Row oldRow, Row newRow) {
try {
return field.get(oldRow).equals(field.get(newRow));
} catch (IllegalAccessException e) {
log.warn("Unexpected error", e);
return false;
}
}
Arrays.stream(fields)
.peek(field -> !field.setAccessible(true))
.filter(field -> rowsAreEqual(field, oldRow, newRow))
...
Notice, that the outer try-catch is not needed.
The argument of Optional#isPresent
is a Consumer<T>
and you put there void
. Although the Consumer#accept
has a void
return type, it must be called first. You need to pass the implementation represented by the Consumer<T>
and not its result:
Arrays.stream(fields)
.peek(field -> field.setAccessible(true))
.filter(field -> rowsAreEqual(field, oldRow, newRow))
.findAny()
.ifPresentOrElse(
field -> newRow.updateACell(),
() -> newRow.udpateACell("new value"));