51

So I am trying to refactor the following code:

/** * Returns the duration from the config file. * * @return The duration. */ private Duration durationFromConfig() { try { return durationFromConfigInner(); } catch (IOException ex) { throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found."); } } /** * Returns the duration from the config file. * * Searches the log file for the first line indicating the config entry for this instance. * * @return The duration. * @throws FileNotFoundException If the config file has not been found. */ private Duration durationFromConfigInner() throws IOException { String entryKey = subClass.getSimpleName(); configLastModified = Files.getLastModifiedTime(configFile); String entryValue = ConfigFileUtils.readFileEntry(configFile, entryKey); return Duration.of(entryValue); } 

I came up with the following to start of with:

private <T> T getFromConfig(final Supplier<T> supplier) { try { return supplier.get(); } catch (IOException ex) { throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found."); } } 

However, it does not compile (obviously), as Supplier cannot throw an IOException. Is there any way I can add that to the method declaration of getFromConfig?

Or is the only way to do it like the following?

@FunctionalInterface public interface SupplierWithIO<T> extends Supplier<T> { @Override @Deprecated default public T get() { throw new UnsupportedOperationException(); } public T getWithIO() throws IOException; } 

Update, I just realised that the Supplier interface is a really simple one, as in it has only the get() method. The original reason why I extended Supplier is to preverse the basic functionality, like the default methods for example.

9
  • Just forget about retaining checked exceptions... use a throws Exception signature in the functional interface and wrap it into an uncheckedCall lambda, which propagates the exception without checking (using sneakyThrow). Commented Mar 27, 2014 at 12:44
  • @MarkoTopolnik That sounds good enough to be an answer (preferably if you'd elaborate a bit on it). I just provided my two cents below but your idea sounds like it could be more accurate Commented Mar 27, 2014 at 12:47
  • 2
    @SimonAndréForsberg JB Nizet already did a fair job here: stackoverflow.com/a/14045585/1103872 I just wouldn't bother wrapping the exception, instead I use sneakyThrow. Commented Mar 27, 2014 at 12:50
  • 2
    This was thoroughly discussed in the lambda mailing list. Some alternative solutions are discussed there in case you want to take a look. The wrapper solution suggested by JB Nizet is also discussed there. Commented Mar 27, 2014 at 13:38
  • 1
    Possible duplicate of Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional? Commented Oct 19, 2017 at 19:36

6 Answers 6

33

Edit

As pointed many times, you don't need any custom class, use Callable and Runnable instead

Wrong, outdated solution

Consider this generic solution:

// We need to describe supplier which can throw exceptions @FunctionalInterface public interface ThrowingSupplier<T> { T get() throws Exception; } // Now, wrapper private <T> T callMethod(ThrowingSupplier<T> supplier) { try { return supplier.get(); } catch (Exception e) { throw new RuntimeException(e); } return null; } // And usage example String methodThrowsException(String a, String b, String c) throws Exception { // do something } String result = callMethod(() -> methodThrowsException(x, y, z)); 
Sign up to request clarification or add additional context in comments.

2 Comments

You don't need to declare ThrowingSupplier. The built-in class Callable already does what you want.
A minor "problem" with Callable is that it throws an Exception. This might have ripple effects on the referencing code. Solutions with generic exception might degrade to RuntimeException avoiding code-bloat in userspace.
23
  • If you would do that, you wouldn't be able to use it as a Supplier as it would only throw UnsupportedOperationException.

  • Considering the above, why not create a new interface and declare the getWithIO method in it?

    @FunctionalInterface public interface SupplierWithIO<T> { public T getWithIO() throws IOException; } 
  • Perhaps some things are better of as old-style Java interfaces? Old-style Java isn't gone just because there's Java 8 now.

4 Comments

I just updated my post with the reasoning behind extending. This method would break if new default methods were added to Supplier. On the other hand, updating the JVM to that version will not break my application either, so no harm done.
The problem with this approach is that this supplier cannot be used with the rest of the JDK Apis expecting an actual Supplier.
You don't need to declare SupplierWithIO. The built-in class Callable already does what you want.
@Gili Technically, Callable declares to throw the more generic Exception If one would want to use it with the other JDK APIs, one might want to go that way.
21

In the lambda mailing list this was throughly discussed. As you can see Brian Goetz suggested there that the alternative is to write your own combinator:

Or you could write your own trivial combinator:

static<T> Block<T> exceptionWrappingBlock(Block<T> b) { return e -> { try { b.accept(e); } catch (Exception e) { throw new RTE(e); } }; } 

You can write it once, in less that the time it took to write your original e-mail. And similarly once for each kind of SAM you use.

I'd rather we look at this as "glass 99% full" rather than the alternative. Not all problems require new language features as solutions. (Not to mention that new language features always causes new problems.)

In those days the Consumer interface was called Block.

I think this corresponds with JB Nizet's answer suggested by Marko above.

Later Brian explains why this was designed this way (the reason of problem)

Yes, you'd have to provide your own exceptional SAMs. But then lambda conversion would work fine with them.

The EG discussed additional language and library support for this problem, and in the end felt that this was a bad cost/benefit tradeoff.

Library-based solutions cause a 2x explosion in SAM types (exceptional vs not), which interact badly with existing combinatorial explosions for primitive specialization.

The available language-based solutions were losers from a complexity/value tradeoff. Though there are some alternative solutions we are going to continue to explore -- though clearly not for 8 and probably not for 9 either.

In the meantime, you have the tools to do what you want. I get that you prefer we provide that last mile for you (and, secondarily, your request is really a thinly-veiled request for "why don't you just give up on checked exceptions already"), but I think the current state lets you get your job done.

Comments

5

Since I have an additional point to make on this subject, I have decided to add my answer.

You have the choice to write a convenience method which either:

  1. wraps a checked-exception-throwing lambda into an unchecked one;
  2. simply calls the lambda, unchecking the exception.

With the first approach, you need one convenience method per functional method signature, whereas with the second approach, you need a total of two methods (except for primitive-returning methods):

static <T> T uncheckCall(Callable<T> callable) { try { return callable.call(); } catch (Exception e) { return sneakyThrow(e); } } static void uncheckRun(RunnableExc r) { try { r.run(); } catch (Exception e) { sneakyThrow(e); } } interface RunnableExc { void run() throws Exception; } 

This allows you to insert a call to one of these methods passing in a nullary lambda, but one which is closing over any arguments of the outside lambda, which you are passing to your original method. This way you get to leverage the automatic lambda conversion language feature without boilerplate.

For example, you can write

stream.forEachOrdered(o -> uncheckRun(() -> out.write(o))); 

compared to

stream.forEachOrdered(uncheckWrapOneArg(o -> out.write(o))); 

I have also found that overloading the wrapping method name for all lambda signatures often leads to ambiguous lambda expression errors. So you need longer distinct names, resulting in ever longer code char-for-char than with the above approach.

In the end, note that the said approach still doesn't preclude writing simple wrapper methods which reuse uncheckedRun/Call, but I found it simply uninteresting because the savings are negligible at best.

Comments

4

I added my own solution, not neccessarilly a direct answer to my question, which introduces the following after some revisions:

@FunctionalInterface public interface CheckedSupplier<T, E extends Exception> { public T get() throws E; } private <R> R getFromConfig(final String entryKey, final Function<String, R> converter) throws IOException { Objects.requireNonNull(entryKey); Objects.requireNonNull(converter); configLastModified = Files.getLastModifiedTime(configFile); return ConfigFileUtils.readFileEntry(configFile, entryKey, converter); } private <T> T handleIOException(final CheckedSupplier<T, IOException> supplier) { Objects.requireNonNull(supplier); try { return supplier.get(); } catch (IOException ex) { throw new IllegalStateException("The config file (\"" + configFile + "\") has not been found."); } } 

This were all one-time only declarations, now I add two variants of the calling code:

private Duration durationFromConfig() { return handleIOException(() -> getFromConfig(subClass.getSimpleName(), Duration::of)); } private int threadsFromConfig() { return handleIOException(() -> getFromConfig(subClass.getSimpleName() + "Threads", Integer::parseInt)); } 

I am not too happy about converting an IOException to an UncheckedIOException, as:

  1. I would need to add an unchecked variant of every method that can throw an IOException.
  2. I prefer obvious handling of the IOException, instead of relying on hoping that you do not forget to catch the UncheckedIOException.

8 Comments

What do you consider as the typical bad consequence of failing to catch UncheckedIOException?
@MarkoTopolnik You can forget to catch them, ultimately leading to a program failure, in a situation where you ought to recover from the situation and continue normal program flow if you had be aware that a IOException could be thrown.
Is it your actual experience that your applications fail at production because you had in mind a way to get around a missing config file, but still forgot to put it in your code? My experience is that these are the most trivial of mistakes, easy to spot, easy to fix, and hard to make in the first place because they don't concern some ill-defined, convoluted, unpredictable sequence of events. They never make it to production, unlike for example NullPointerExceptions, which often occur in the above-described way.
Thankfully, I see quite explicit pressure in the core team to remove exception checking altogether from a future version of Java. So even the core team can't find in their hearts a truly justified reason to go on against the tides.
@MarkoTopolnik With a config file in this scenario, no. With other situations that could lead to IOExceptons, I would be a bit scared of them. Out of curiosity, where do you find the news of the future of Java? I'd be interested to at least read, or possibly join in, the discussions. Sadly there are tons of mailing lists and it is hard to pick a correct one.
|
0

We can use this generic solution also. Any type of exception we handle by this approach.

 @FunctionalInterface public interface CheckedCall<T, E extends Throwable> { T call() throws E; } public <T> T logTime(CheckedCall<T, Exception> block) throws Exception { Stopwatch timer = Stopwatch.createStarted(); try { T result = block.call(); System.out.println(timer.stop().elapsed(TimeUnit.MILLISECONDS)); return result; } catch (Exception e) { throw e; } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.