Java 8 Method Reference Unhandled Exception

Java 8 method reference unhandled exception

If you look at the Consumer<T> interface, the accept method (which is what your method reference would effectively be using) isn't declared to throw any checked exceptions - therefore you can't use a method reference which is declared to throw a checked exception. The enhanced for loop is okay, because there you're always in a context where SomeException can be thrown.

You could potentially create a wrapper which converts the checked exception to an unchecked exception, and throw that. Alternatively, you could declare your own functional interface with an accept() method which does throw a checked exception (probably parameterizing the interface with that exception), and then write your own forEach method that takes that functional interface as an input.

Why can't I throw an exception in a Java 8 lambda expression?

You are not allowed to throw checked exceptions because the accept(T t, U u) method in the java.util.function.BiConsumer<T, U> interface doesn't declare any exceptions in its throws clause. And, as you know, Map#forEach takes such a type.

public interface Map<K, V> {

default void forEach(BiConsumer<? super K, ? super V> action) { ... }

} |
|
V
@FunctionalInterface
public interface BiConsumer<T, U> {

void accept(T t, U u); // <-- does throw nothing

}

That is true when we are talking about checked exceptions. But you still can throw an unchecked exception (e.g. a java.lang.IllegalArgumentException):

new HashMap<String, String>()
.forEach((a, b) -> { throw new IllegalArgumentException(); });

Why does a method reference to ctor that throws ... throw as well?

You need to provide a custom interface ThrowingFunction which has one method that throws Exception.

public interface ThrowingFunction<ParameterType, ReturnType> {
ReturnType invoke(ParameterType p) throws Exception;
}

public class Mcve {
public Mcve(String s) throws Exception {
// whatever
}
public static void main(String[] args) {
ThrowingFunction<String, Mcve> mcveFactory = Mcve::new;
}
}

Using this approach results in calling mcveFactory.invoke("lalala"); forcing you to handle the exception thrown by the constructor.

Reason for the error is that the actual function reference you want to store (not 100% sure about the terminology) throws an exception and therefore the types simply do not match up. If you could store Mcve::new inside a function then whoever calls the function no longer knows an Exception can be thrown. What would then happen if the exception would actually be thrown? Both throwing the exception and discarding it do not work.


Alternative: if you need to actually retrieve a Function<String, Mcve> in the end then you need to write a function (or lambda) that invokes the constructor, catches the exception and either discards it or rethrows it wrapped inside a unchecked RuntimeException.

public class Mcve {
public Mcve(String s) throws Exception {
// whatever
}

public static void main(String[] args) {
Function<String, Mcve> mcveFactory = parameter -> {
try {
return new Mcve(parameter);
} catch (Exception e) {
throw new RuntimeException(e); // or ignore
}
};
}
}

I would argue that the error message itself is at least a bit misleading since you normally see it when actually invoking the method. I can certainly understand the confusion resulting in the first sub-question. It would be clearer (sadly not possible) to state something like

Incompatible types Function<String,Mcve> vs. Function<String,Mcve> throws Exception.

Java method references while exception handling

My testCacheExpiration method signature doesn't conform to the run method's signature in runnable interface (which doesn't throw any exception)

Following changes solved it:

@FunctionalInterface
private interface ThrowingRunnable <E extends Exception> {
void run() throws Exception;
}

private Runnable handleNonTestException(ThrowingRunnable<? extends Exception> r) {
return () -> {
try {
r.run();
} catch (Exception e) {
logger.error(NON_TEST_EXCEPTION_MSG, e);
errors.add(new Error(e.getMessage(), NON_TEST_EXCEPTION_MSG));
}
};
}`

Java 8: How do I work with exception throwing methods in streams?

You need to wrap your method call into another one, where you do not throw checked exceptions. You can still throw anything that is a subclass of RuntimeException.

A normal wrapping idiom is something like:

private void safeFoo(final A a) {
try {
a.foo();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}

(Supertype exception Exception is only used as example, never try to catch it yourself)

Then you can call it with: as.forEach(this::safeFoo).

How can I throw CHECKED exceptions from inside Java 8 lambdas/streams?

This LambdaExceptionUtil helper class lets you use any checked exceptions in Java streams, like this:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());

Note Class::forName throws ClassNotFoundException, which is checked. The stream itself also throws ClassNotFoundException, and NOT some wrapping unchecked exception.

public final class LambdaExceptionUtil {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}

@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try { consumer.accept(t); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try { biConsumer.accept(t, u); }
catch (Exception exception) { throwAsUnchecked(exception); }
};
}

/** .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName)) */
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try { return function.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
};
}

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
{
try { t.run(); }
catch (Exception exception) { throwAsUnchecked(exception); }
}

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
{
try { return supplier.get(); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try { return function.apply(t); }
catch (Exception exception) { throwAsUnchecked(exception); return null; }
}

@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }

}

Many other examples on how to use it (after statically importing LambdaExceptionUtil):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());

List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}

@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

Class clazz2 = uncheck(Class::forName, "java.lang.String");
}

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}

UPDATE as of Nov 2015 The code has been improved with the help of @PaoloC, please check his answer below and upvote it. He helped solve the last problem: Now the compiler will ask you to add throw clauses and everything's as if you could throw checked exceptions natively on Java 8 streams.


NOTE 1 The rethrow methods of the LambdaExceptionUtil class above may be used without fear, and are OK to use in any situation.


NOTE 2: The uncheck methods of the LambdaExceptionUtil class above are bonus methods, and may be safely removed them from the class if you don't want to use them. If you do used them, do it with care, and not before understanding the following use cases, advantages/disadvantages and limitations:

• You may use the uncheck methods if you are calling a method which literally can never throw the exception that it declares. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome: String text = uncheck(() -> new String(byteArr, "UTF-8"));

• You may use the uncheck methods if you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate. Wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run(), which does not throw any checked exceptions.

• In any case, if you decide to use the uncheck methods,
be aware of these 2 consequences of throwing CHECKED exceptions without a throws clause: 1) The calling-code won't be able to catch it by name (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement). It will bubble and probably be caught in the main program loop by some "catch Exception" or "catch Throwable", which may be what you want anyway. 2) It violates the principle of least surprise: it will no longer be enough to catch RuntimeException to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.

  • References:
  • http://www.philandstuff.com/2012/04/28/sneakily-throwing-checked-exceptions.html
  • http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
  • Project Lombok annotation: @SneakyThrows
  • Brian Goetz opinion (against) here: How can I throw CHECKED exceptions from inside Java 8 streams?
  • https://softwareengineering.stackexchange.com/questions/225931/workaround-for-java-checked-exceptions?newreg=ddf0dd15e8174af8ba52e091cf85688e *

Java 8 method reference unhandled exception

If you look at the Consumer<T> interface, the accept method (which is what your method reference would effectively be using) isn't declared to throw any checked exceptions - therefore you can't use a method reference which is declared to throw a checked exception. The enhanced for loop is okay, because there you're always in a context where SomeException can be thrown.

You could potentially create a wrapper which converts the checked exception to an unchecked exception, and throw that. Alternatively, you could declare your own functional interface with an accept() method which does throw a checked exception (probably parameterizing the interface with that exception), and then write your own forEach method that takes that functional interface as an input.

Java 8 Lambda function that throws exception?

You'll need to do one of the following.

  • If it's your code, then define your own functional interface that declares the checked exception:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
    R apply(T t) throws IOException;
    }

    and use it:

    void foo (CheckedFunction f) { ... }
  • Otherwise, wrap Integer myMethod(String s) in a method that doesn't declare a checked exception:

    public Integer myWrappedMethod(String s) {
    try {
    return myMethod(s);
    }
    catch(IOException e) {
    throw new UncheckedIOException(e);
    }
    }

    and then:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    or:

    Function<String, Integer> f =
    (String t) -> {
    try {
    return myMethod(t);
    }
    catch(IOException e) {
    throw new UncheckedIOException(e);
    }
    };


Related Topics



Leave a reply



Submit