title | description |
---|---|
Page title |
A Java library to handle success and failure without exceptions |
🏠 Introduction
Result
objects have all the answers you need
The purpose of this library is to type-safely encapsulate the output of operations that may succeed or fail, instead of throwing exceptions.
If you like Optional
but feel that it sometimes falls too short, you will feel right at home.
Result Library in a Nutshell
Before Result
, we would wrap the invocation of an exception-throwing method foo
inside a try
block so that errors can be handled inside a catch
block.
public int getFooLength() {
int length;
try {
String result = foo();
this.ok(result);
length = result.length();
} catch(SomeException problem) {
this.error(problem);
length = -1;
}
return length;
}
This approach is lengthy, and that's not the only problem — it's also very slow. Conventional wisdom says that exceptional logic shouldn't be used for normal program flow. Result
makes us deal with expected, non-exceptional error situations explicitly as a way to enforce good programming practices and make our programs run faster.
Let's now look at how the above code could be refactored if foo
returned a result object instead of throwing an exception:
public int getFooLength() {
Result<String, SomeFailure> result = foo();
result.ifSuccessOrElse(this::ok, this::error);
Result<Integer, SomeFailure> resultLength = result.mapSuccess(String::length);
return resultLength.orElse(-1);
}
In the above example, we use only four lines of code to replace the ten that worked in the first example. But we can make it even shorter by chaining methods in typical functional programming style:
public int getFooLength() {
return foo().ifSuccessOrElse(this::ok, this::error).mapSuccess(String::length)
.orElse(-1);
}
In fact, since we are using -1
here just to signal that the underlying operation failed, we'd be better off returning a Result
object upstream:
public Result<Integer, SomeFailure> getFooLength() {
return foo().ifSuccessOrElse(this::ok, this::error).mapSuccess(String::length);
}
This allows others to easily compose operations on top of ours, just like we did with foo
.