-
Notifications
You must be signed in to change notification settings - Fork 0
API Examples
This document details the basic use cases of Unchecked Java.
Warning
Remember to read Unchecked Java's Safety Guide before using Unchecked Java.
Let's say we were defining a private static final
url field. You may start with the following:
private static final URL GOOGLE = new URL("https://www.google.com");
Your IDE would quickly complain about an uncaught exception. The problem is that new URL
throws a MalformedURLException
which is a checked exception. At this point most people just revert to using a String
:
private static final String GOOGLE = "https://www.google.com";
In the grand scheme of things, this works just fine, with the likely risk that you'll create multiple redundant URL google = new URL(GOOGLE)
instances in different parts of your code. But what if you wanted to enforce a singleton URL instance?
private static final URL GOOGLE;
static {
try {
GOOGLE = new URL("https://www.google.com");
} catch (final MalformedURLException e) {
throw new AssertionError(e); // cannot happen
}
}
This is perfectly functional if not cumbersome and verbose. Using Unchecked Java we can accomplish this with a single line of code:
import static software.leonov.common.util.function.CheckedSupplier.uncheckedGet;
...
private static final URL GOOGLE = uncheckedGet(() -> new URL("https://www.google.com"));
Note
Even if an error occurs initializing a static field, we are not worried about circumventing Java's exception handling mechanisms with Unchecked Java because all class initialization errors will be suppressed under a java.lang.ExceptionInInitializerError
.
How many times have you seen code like this:
final byte[] bytes = ...
try {
final String content = new String(bytes, "UTF-8");
} catch (final UnsupportedEncodingException e) {
throw new AssertionError(e); // cannot happen
}
All Java implementations are required to support the UTF-8 charset. With Unchecked Java this code can again be reduced to a one-liner:
import static software.leonov.common.util.function.CheckedSupplier.uncheckedGet;
...
final String content = uncheckedGet(() -> new String(bytes, "UTF-8"));
Java's functional interface types are not compatible with lambdas that throw checked exceptions. Consider the following example:
final List<URL> resources = Stream.of("https://www.google.com", "https://www.yahoo.com").map(URL::new).collect(Collectors.toList());
The Stream.map
method accepts a java.util.function.Function
object whose apply
method does not throw a checked exception. We are forced to refactor the code and rethrow the MalformedURLException
as a RuntimeException
in order to get it to compile:
final List<URL> resources = Stream.of("https://www.google.com", "https://www.yahoo.com").map(t -> {
try {
return new URL(t);
} catch (final MalformedURLException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
With Unchecked Java the example above can be re-written in a compact and concise manner:
import static software.leonov.common.util.function.CheckedFunction.unchecked;
...
final List<URL> resources = Stream.of("https://www.google.com", "https://www.yahoo.com").map(unchecked(URL::new)).collect(Collectors.toList());
Let's look at another example. Suppose you want to sort files according to their size in bytes:
final List<Path> files = ...
files.sort(Comparator.comparingLong(value -> {
try {
return Files.size(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
}));
With Unchecked Java we can write:
import static software.leonov.common.util.function.CheckedComparator.unchecked;
import static software.leonov.common.util.function.CheckedComparator.comparingLong;
...
final List<Path> files = ...
files.sort(unchecked(comparingLong(Files::size)));
Unchecked Java provides an explicit method to propagate Checked Exceptions as unchecked. It is explicitly designed for cases where you are sure the caller will handle all possible exceptions thrown by this method.
Warning
Another reminder to read Unchecked Java's Safety Guide before using Unchecked Java.
Suppose you are submitting tasks which perform I/O operations to an executor service.
import static software.leonov.common.util.function.Exceptions.uncheckedException;
...
final ExecutorService svc = ...
...
final Future<?> future = svc.submit(() -> {
final Path path = ...
...
try (final Reader out = Files.newBufferedReader(path)) {
...
} catch (final IOException e) {
throw Unchecked.exception(e); // rethrow the exception without wrapping it in a RuntimeException
}
});
We rethrew the IOException
as unchecked because we are guaranteed that it will be stored in the returned Future
.
try {
future.get();
} catch (final ExecutionException e) {
assert (e.getCause() instanceof IOException);
}
In this case wrapping it in a RuntimeException
would server no purpose and simply add noise to the stack trace.