48

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.

skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 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`). – Marko Topolnik Mar 27 '14 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 – Simon Forsberg Mar 27 '14 at 12:47
  • 2
    @SimonAndréForsberg JB Nizet already did a fair job here: http://stackoverflow.com/a/14045585/1103872 I just wouldn't bother wrapping the exception, instead I use `sneakyThrow`. – Marko Topolnik Mar 27 '14 at 12:50
  • @MarkoTopolnik Can you eloborate more on what `uncheckedCall` and `sneakyThrow` are in your example? – skiwi Mar 27 '14 at 12:52
  • `static T uncheckCall(Callable callable) { try { return callable.call(); } catch (Exception e) { return sneakyThrow(e); }`. `sneakyThrow` is a standard idiom and can be found for example here: http://stackoverflow.com/questions/14038649/java-sneakythrow-of-exceptions-type-erasure – Marko Topolnik Mar 27 '14 at 12:54
  • @MarkoTopolnik Also, I think this really *is* an issue with lambdas in Java 8, hence why people are having issues with it. Yet, I am much much much happier with lambdas with a minor issue, than with no lambdas at all. – skiwi Mar 27 '14 at 12:54
  • I agree it's an issue, but I call out checked exceptions as the drag, not lambdas :) – Marko Topolnik Mar 27 '14 at 12:54
  • 2
    This was [thoroughly discussed in the lambda mailing list](http://mail.openjdk.java.net/pipermail/lambda-dev/2013-January/007653.html). 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. – Edwin Dalorzo Mar 27 '14 at 13:38
  • 1
    Possible duplicate of [Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?](https://stackoverflow.com/questions/14039995/java-8-mandatory-checked-exceptions-handling-in-lambda-expressions-why-mandato) – rogerdpack Oct 19 '17 at 19:36

6 Answers6

26

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));
Vladimir
  • 281
  • 3
  • 6
  • 12
    You don't need to declare `ThrowingSupplier`. The built-in class `Callable` already does what you want. – Gili Mar 06 '18 at 03:40
  • 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. – Adam Kotwasinski Mar 09 '23 at 23:03
21
  • 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.

Simon Forsberg
  • 13,086
  • 10
  • 64
  • 108
  • 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. – skiwi Mar 27 '14 at 12:48
  • 14
    The problem with this approach is that this supplier cannot be used with the rest of the JDK Apis expecting an actual Supplier. – Edwin Dalorzo Mar 27 '14 at 13:36
  • 6
    You don't need to declare `SupplierWithIO`. The built-in class `Callable` already does what you want. – Gili Mar 06 '18 at 03:41
  • 3
    @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. – Simon Forsberg Mar 06 '18 at 11:11
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.

Community
  • 1
  • 1
Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
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.
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • What do you consider as the typical bad consequence of failing to catch `UncheckedIOException`? – Marko Topolnik Mar 28 '14 at 07:49
  • @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. – skiwi Mar 28 '14 at 09:35
  • 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. – Marko Topolnik Mar 28 '14 at 09:39
  • 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. – Marko Topolnik Mar 28 '14 at 09:40
  • @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. – skiwi Mar 28 '14 at 09:42
  • Just in the discussion linked to by Edwin here. BTW have you really never experienced hard-to-localize production errors, which ultimately originate with inappropriate checked exception handling? `catch (IOException e) {` umm... what now? Let's leave it to Eclipse: `e.printStackTrace(); }` Advanced version: `catch (IOException e) { log.error(e); }` Advanced-plus version: `catch (IOException e) { log.error("IO failure", e)`---now I at least get a stacktrace in the log. – Marko Topolnik Mar 28 '14 at 09:47
  • Ultra-brainy version: `catch (IOException e) { log.error("failure",e); throw new RuntimeException(e);}`---now it's "just" contaminating the log with duplicated exception reports. On the other hand, the worst that can happen when an IOException is thrown and not handled, is a clean abort of the current unit of work. That's exactly what I wanted, in 99% of cases. In the remaining 1%, I know very well what I want to do with it and don't gratuitously forget about it. – Marko Topolnik Mar 28 '14 at 09:47
  • You don't need to declare `CheckedSupplier`. The built-in class `Callable` already does what you want. – Gili Mar 06 '18 at 03:40
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;
        }
    }