3

Assuming I have to read from a file, and then construct a java object out of it.

PersonData p = new PersonData();
p.setName(readTokenAsString());
p.setAge(AgeConverter.createFromDateOfBirth(readTokenAsString()));  // this throws a checked exception if the date of birth is mal-formed.

//... a list of methods that throws exception as AgeConverter

Behavior I want: If one attribute has problem, just ignore it and keep process other attributes.

Solution I can think of:

try {
  p.setAge1(...);
} catch (Exception e) {
  //log and ignore
}

try {
  p.setAge2(...);
} catch (Exception e) {
  //log and ignore
}

//repeat for each attribute

Question:

Is there better way to do this to avoid repetition? Functional style maybe?

a) What's the best approach if I cannot modify PersonData class.
b) What's the best approach if I can rewrite PersonData class.

Weishi Z
  • 1,629
  • 5
  • 18
  • 38
  • Consider Reflection ion java, a bit more effort initially but will be much more convenient and reusable in long run – akshaya pandey Nov 06 '18 at 03:53
  • `AgeConverter.createFromDateOfBirth(readTokenAsString())` should modify this method to swallow Exception? or you can wrap it inside another method that swallows exception? Therefore you won't need to try/catch everywhere. – Dang Nguyen Nov 06 '18 at 03:56
  • 1
    if you can update PersonData class, better make it immutable, have a Builder class that sets all values via all args constructor – sidgate Nov 06 '18 at 04:05
  • You can also use solution provided by this: [https://stackoverflow.com/a/28659553/6648303](https://stackoverflow.com/a/28659553/6648303) – f.ald Nov 06 '18 at 05:12

3 Answers3

2

Given your current declaration, I would do it as follows.

Define a @FunctionalInterface to which you can pass your I/O logic:

@FunctionalInterface
public interface CheckedSupplier<T> {
    T getValue() throws Exception;
}

Define an utility method that consumes the @FunctionaInterface:

public static final <T> T getValueWithDefault(CheckedSupplier<T> supplier, T defaultValue) {
    try {
        return supplier.getValue();
    } catch (Exception e){
        return defaultValue;
    }
}

Use the utility method as follows:

PersonData p = new PersonData();
p.setName(getValueWithDefault(() -> readTokenAsString(), "default"));
p.setAge(getValueWithDefault(() -> AgeConverter.createFromDateOfBirth(readTokenAsString()), 0));

This should do the trick regardless of weather you want modify the PersonData class or not.

  • 1
    I ended up using vavr's checked exception interface. It's basically the same idea as yours. https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/CheckedFunction0.html#liftTry-io.vavr.CheckedFunction0- – Weishi Z Nov 06 '18 at 06:01
1

If you use Java 8 you can do something like this. Create your own functional interface with one method that throws Exception

public interface MyConsumer<T> {
    public void process(T t) throws Exception;
}

And create a static method to use that interface

public static <T> void setAndLogException(T value, MyConsumer<T> consumer) {
  try {
    consumer.process(value);
  } catch (Exception e) {
  // log exception
  }
}

And then using it like setAndLogException(AgeConverter.createFromDateOfBirth(readTokenAsString()), p::setAge);

Ivan
  • 8,508
  • 2
  • 19
  • 30
  • Just tried it. For a checked exception, it seems the compiler still complains about not handling it. – Weishi Z Nov 06 '18 at 05:02
  • This helped me to get the idea. However, this would still have compiler error. As long as you're calling `AgeConverter.XXX`, you'll need to handle the checked exception inside the calling method. So we'll need a `MySupplier` interface throwing exception to wrap around the `AgeConverter.XXX` method. (instead of wrapping around p.setAge method.) – Weishi Z Nov 06 '18 at 06:12
0

You can also use solution provided by this: https://stackoverflow.com/a/28659553/6648303

This solution won't complain at compile phase about checked Exceptions. It would be something like this:

public static void ignoringExc(RunnableExc r) {
  try { r.run(); } catch (Exception e) { }
}

@FunctionalInterface public interface RunnableExc { void run() throws Exception; }

and then:

PersonData p = new PersonData();
ignoringExc(() -> p.setName(readTokenAsString()));
...
f.ald
  • 320
  • 1
  • 16