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
whoserun
method may throw any exceptions. Then implementpublic 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 ofRunnableEx?
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();
}
}
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 catch
block (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:
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 Runnable
as an argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With