Better way to continue after exceptions in java

Viewed 191

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.

3 Answers

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.

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);

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()));
...
Related