LeakyAbstractions / result-manual

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

title description
Page title
A Java library to handle success and failure without exceptions

🏠 Introduction

Result is a Java library to handle success and failure without exceptions.

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.

About