Creating Results

How to instantiate new Result objects

There are several ways to create result objects.

Successful Results

A successful result contains a non-null value produced by an operation when everything works as intended. We can use Results::success to create a new instance.

@Test
void testSuccess() {
  // When
  Result<Integer, ?> result = Results.success(200);
  // Then
  assertTrue(result::hasSuccess);
  assertFalse(result::hasFailure);
}

Note that we can invoke Result::hasSuccess or Result::hasFailure to check whether a result is successful or failed (more on this in the next section).

Failed Results

On the other hand, a failed result holds a value representing the problem that prevented the operation from completing. We can use Results::failure to create a new one.

@Test
void testFailure() {
  // When
  Result<?, String> result = Results.failure("The operation failed");
  // Then
  assertTrue(result::hasFailure);
  assertFalse(result::hasSuccess);
}

Failure values cannot be null either.

Results Based on Nullable Values

When we need to create results that depend on a possibly null value, we can use Results::ofNullable. If the first argument is null, then the second one will be used to create a failed result.

@Test
void testOfNullable() {
  // Given
  String string1 = "The operation succeeded";
  String string2 = null;
  // When
  Result<String, Integer> result1 = Results.ofNullable(string1, 404);
  Result<String, Integer> result2 = Results.ofNullable(string2, 404);
  // Then
  assertTrue(result1::hasSuccess);
  assertTrue(result2::hasFailure);
}

The second argument can be either a failure value or a function that produces a failure value.

Results Based on Optionals

We can also use Results::ofOptional to create results that depend on an Optional value. If the first argument is an empty optional, then the second one will be used to create a failed result.

@Test
void testOfOptional() {
  // Given
  Optional<BigDecimal> optional1 = Optional.of(BigDecimal.ONE);
  Optional<BigDecimal> optional2 = Optional.empty();
  // When
  Result<BigDecimal, Integer> result1 = Results.ofOptional(optional1, -1);
  Result<BigDecimal, Integer> result2 = Results.ofOptional(optional2, -1);
  // Then
  assertTrue(result1::hasSuccess);
  assertTrue(result2::hasFailure);
}

The second argument can be a Supplier too.

Results Based on Callables

Finally, if we have a task that may either return a success value or throw an exception, we can encapsulate it as a result using Results::ofCallable so we don't need to use a try-catch block.

String task1() {
  return "OK";
}

String task2() throws Exception {
  throw new Exception("Whoops!");
}

@Test
void testOfCallable() {
  // When
  Result<String, Exception> result1 = Results.ofCallable(this::task1);
  Result<String, Exception> result2 = Results.ofCallable(this::task2);
  // Then
  assertTrue(result1::hasSuccess);
  assertTrue(result2::hasFailure);
}

This method enables compatibility with legacy or third-party code that uses exceptions to indicate operation failure.

Conclusion

We've covered how to create new instances of Result using various factory methods provided by the Results class. Each method serves a specific purpose, allowing you to select the most suitable one based on the situation.

Last updated

Logo

Copyright 2024 Guillermo Calvo