A Java library to handle success and failure without exceptions
The purpose of this library is to provide a type-safe encapsulation of operation results that may have succeeded or failed, instead of throwing exceptions.
If you like Optional
but feel that it sometimes falls too short, you'll love Result
.
The best way to think of Result is as a super-powered version of Optional. The only difference is that whereas Optional may contain a successful value or express the absence of a value, Result contains either a successful value or a failure value that explains what went wrong.
Result
objects have methods equivalent to those in
Optional
, plus a few more to handle failure values.
Optional | Result |
---|---|
isPresent |
hasSuccess |
isEmpty |
hasFailure |
get |
getSuccess |
getFailure |
|
orElse |
orElse |
orElseGet |
orElseMap |
orElseThrow |
|
stream |
streamSuccess |
streamFailure |
|
ifPresent |
ifSuccess |
ifFailure |
|
ifPresentOrElse |
ifSuccessOrElse |
filter |
filter |
recover |
|
map |
mapSuccess |
mapFailure |
|
map |
|
flatMap |
flatMapSuccess |
or |
flatMapFailure |
flatMap |
Before Result, we would wrap exception-throwing foobar
method invocation inside a try
block so that errors can be
handled inside a catch
block.
public int getFoobarLength() {
int length;
try {
final String result = foobar();
this.commit(result);
length = result.length();
} catch(SomeException problem) {
this.rollback(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 of enforcing good programming practices.
Let's now look at how the above code could be refactored if method foobar
returned a Result object instead of
throwing an exception:
public int getFoobarLength() {
final Result<String, SomeFailure> result = foobar();
result.ifSuccessOrElse(this::commit, this::rollback);
final 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 getFoobarLength() {
return foobar().ifSuccessOrElse(this::commit, this::rollback).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> getFoobarLength() {
return foobar().ifSuccessOrElse(this::commit, this::rollback).mapSuccess(String::length);
}
This allows others to easily compose operations on top of ours, just like we did with foobar
.
Please read the Quick Guide to know how to add this library to your build.
This library adheres to Pragmatic Versioning.
Artifacts are available in Maven Central.
Here you can find the full Javadoc documentation.
You may want to visualize the latest benchmark report.
We'd love to help. Check out the support guidelines.
If you'd like to contribute to this project, please start here.
This project is governed by the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.
Copyright 2024 Guillermo Calvo.
This library is licensed under the Apache License, Version 2.0 (the "License"); you may not use it except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
Permitted:
- Commercial Use: You may use this library and derivatives for commercial purposes.
- Modification: You may modify this library.
- Distribution: You may distribute this library.
- Patent Use: This license provides an express grant of patent rights from contributors.
- Private Use: You may use and modify this library without distributing it.
Required:
- License and Copyright Notice: If you distribute this library you must include a copy of the license and copyright notice.
- State Changes: If you modify and distribute this library you must document changes made to this library.
Forbidden:
- Trademark use: This license does not grant any trademark rights.
- Liability: The library author cannot be held liable for damages.
- Warranty: This library is provided without any warranty.