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?
CodePudding user response:
The base of the code is not compilable.
Stream#forEach
returnsvoid
, therefore you cannot performStream#filter
on that. Use eitherStream#peek
(please, read this) orStream#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 aConsumer<T>
and you put therevoid
. Although theConsumer#accept
has avoid
return type, it must be called first. You need to pass the implementation represented by theConsumer<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"));