4

From the book Java 8 for the impatient by Cay Horstmann:

Didn’t you always hate it that you had to deal with checked exceptions in a Runnable? Write a method uncheck that catches all checked exceptions and turns them into unchecked exceptions. For example,

new Thread(uncheck(
() -> { System.out.println("Zzz"); Thread.sleep(1000); })).start();
// Look, no catch (InterruptedException)!

Hint: Define an interface RunnableEx whose run method may throw any exceptions. Then implement

public static Runnable uncheck(RunnableEx runner)

Use a lambda expression inside the uncheck function.

For which I code like this:

public interface RunnableEx {
    void run() throws Exception;
}

public class TestUncheck {
    public static Runnable uncheck(RunnableEx r) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    r.run();
                } catch (Exception e) {
                }
            }
        };
    }

    public static void main(String[] args) {
        new Thread(uncheck(
                () -> {
                    System.out.println("Zzz");
                    Thread.sleep(1000);
                }
        )).start();
    }
}

Did I do according to what was hinted? Is there a better way of doing this?

Also there is another complementary question:

Why can’t you just use Callable<Void> instead of RunnableEx?

MikaelF
  • 3,518
  • 4
  • 20
  • 33
user1539343
  • 1,569
  • 6
  • 28
  • 45

2 Answers2

4

Your code fails to wrap the checked exception into an unchecked one. Further, it doesn’t following the hint to use a lambda expression. A corrected version would be:

public static Runnable uncheck(RunnableEx r) {
    return () -> {
        try {
            r.run();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

There are indeed possibilities to improve this further. Since the whole purpose of the RunnableEx interface is to wrap a Runnable, you could place the factory method inside the interface itself:

public interface RunnableEx {
    void run() throws Exception;
    public static Runnable uncheck(RunnableEx r) {
        return () -> {
            try {
                r.run();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

Of course, now your calling code has to be adapted to:

public static void main(String[] args) {
    new Thread(RunnableEx.uncheck(
            () -> {
                System.out.println("Zzz");
                Thread.sleep(1000);
            }
    )).start();
}

Now, the interface itself could implement the wrapping feature, becoming compatible with Runnable, which would eliminate the need to have a Runnable and a RunnableEx instance to represent a single action:

public interface RunnableEx extends Runnable {
    void runWithException() throws Exception;
    @Override default void run() {
            try {
                runWithException();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    }
    public static Runnable uncheck(RunnableEx r) {
        return r;
    }
}

Note that while the calling code doesn’t change syntactically, it will implicitly create a Runnable instance in the first place, when creating the RunnableEx instance, that’s why uncheck becomes a no-op, merely serving as a marker that a RunnableEx should be created rather than a Runnable. With this interface definition, you could achieve the same via

public static void main(String[] args) {
    new Thread( (RunnableEx) () -> {
            System.out.println("Zzz");
            Thread.sleep(1000);
        }
    ).start();
}

The reason why you can’t “just use” a Callable<Void> here, is that Callable<Void> still is a Callable, requiring the implementation code to return a value. In other words, there is no implicit conversion from void to Void. So you can use it, but the lambda expression would require a return null; statement at its end then (null is the only value compatible with Void).

public class TestUncheck {
    public static Runnable uncheck(Callable<Void> r) {
        return () -> {
            try { r.call(); }
            catch (Exception e) { throw new RuntimeException(e); }
        };
    }

    public static void main(String[] args) {
        new Thread(uncheck(
                () -> {
                    System.out.println("Zzz");
                    Thread.sleep(1000);
                    return null;
                }
        )).start();
    }
}
Holger
  • 285,553
  • 42
  • 434
  • 765
1

This way of catching exceptions from lambdas works fine, and is (with a few additions) the one implemented in the Unchecked class from jOOλ, a popular Java library.

For comparison, see their static method Unchecked.runnable, which is identical to your solution, with the exception of their Exception handler and to the lambda abreviating the return value.

With regards to the assignment from Java 8 to the Impatients, your uncheck method does not use a lambda (for this, see the Unchecked.runnable method), and does not throw an unchecked exception. For this, your catchblock (currently empty) needs to wrap the exception into an unchecked exception. There are many ways to do it, and many questions on SO are relevant:

  1. How to wrap checked exceptions but keep the original runtime exceptions in Java
  2. Replace a checked exception with a runtime exception?
  3. Handling Runtime exception in Java

But a simple way is:

throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);

Finally, to answer the Callable<Void>question, I am assuming you mean "instead of Runnable"? If so, it is because the constructors from Thread take a Runnableas an argument.

Community
  • 1
  • 1
MikaelF
  • 3,518
  • 4
  • 20
  • 33