Home > Blockchain >  Call function in Java Optional ifPresent with OrElse
Call function in Java Optional ifPresent with OrElse

Time:06-21

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:

  1. 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))
          ...
    
  2. 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.

  3. 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"));
    
  • Related