Compensate a lack in Java: The class Result

Dynamic structure
Image source: PIRO4D pixabay.com

In some my previous posts I wrote about the advantages of Java 8 Optional<T> compared to using null as the value of the object.
Optional<T> allows us to solve elegantly and effectively the problem when the processed objects or their parts can be present or absent.
In this post, we will make a step further and consider the situations when our processing (for example – computing, reading or writing an object on a medium, etc.) completes as success or as failure.

In the example with a boiler, considered in
Java 8 Optional and objects with dynamic structure. Part 2
we got boiled water, if as input we have both water and electricity. In the absence of water or electricity, we received an Optional.empty () object.
In the case of a boiler, the cause of the failure is not difficult to determine. But if our device prepares different types of coffee, in case of failure it would be good to know what is the reason. For example, which ingredient is missing.
To do this, we need to be able to pack into return value the result of the processing in the case of success and information about the problems in the case of failure.
Unfortunately, Java allows us to use strictly one object as the return value of the method. But we need to be able to return either one or another object.

With all due respect to the designers of Java and understanding the complexity of decision process by expanding the language with new features, I think it’s one of the shortcomings of the language, which must be fix. For example, as it is done in Scala or in Kotlin.

But until it’s done, I suggest using the class Result<SUCCESS, FAILURE>. Its development was inspired by reading one of the chapters of the book:

Functional Programming in Java
How functional techniques improve your Java programs
Pierre-Yves Saumont
Manning Pubn
ISBN 9781617292736 472 pages

I strongly recommend you to read this book to you, if you have not read it yet.
So, let’s move on to the class.

How do I use it inside a method (as a return value)?

The class allows you to pack in its instances the result of processing in case of success and information about the error in case of failure. Therefore the class is parameterized by two types:  Result<SUCCESS, FAILURE>.

The trick is that it allows you to pack either one or the other, but not both.

How to use it outside (by caller)?

To process the result, you can use a simple or more elegant approach, depending on the situation.
A simple approach has two steps. First you determine whether the processing was successful. And then, depending on this, you process separately either a positive result or an error.
An elegant approach is to use functional programming elements from the Java 8 arsenal.

But let’s get to the examples.

Modification of a boiler

We will start with our model of boiler from the example

Java 8 Optional and objects with dynamic structure. Part 2

The source code of example you can found on GitHub:

https://github.com/vsirotin/Smartenesse-Java

In the example, we tried using Java 8 Optional to simulate the operation of a stationary boiler, which can be seemed in the offices of some companies.

Its scheme is shown in the picture below:

As you can see, the boiler requires water and electricity. As ouput, the boiler can produce cold or boiled water.
We will model the cold water in the class:

public class CupOfWater {
	public CupOfBoiledWater boil() {
    return new CupOfBoiledWater();}
}

As we can see, we can get boiled water from cold water using the method boil().
The class simulating boiled water is quite simple:

public class CupOfBoiledWater {}

Thus, the input of this device can be described by the interface:

public interface IBoilerInput2 {
   
   void setAvailability(@Nullable CupOfWater water, boolean powerAvailable);
}

Please, do not be surprised at the suffix numbers in the names of the interfaces. In the series, I considered various alternatives to the implementation of the teapot.
We define the output of the device like this:

public interface IBoilerOutput3 {
   
   Result<CupOfWater, String> getCupOfWater();

   Result<CupOfBoiledWater, String> getCupOfBoiledWater();

}

As you can see, by success, we get an object that simulates cold or boiled water. And in case of failure – the text about the error. Of course, instead of text, we could use a more complex object with an error code, and so on.
The behavior of the class is described by the interface:

interface IBoiler3 extends IBoilerInput2, IBoilerOutput3 {}

Compared to the old version, it can be seen that instead of the Optional<CupOfBoiledWater> we use now Result<CupOfBoiledWater, String>

Writing an JUnit test:

public class Boiler3Test {
	
	private IBoiler3 boiler;
	
	@Before
	public void setUp() throws Exception {
		boiler = new Boiler3();
	}

	@Test
	public void testBothNotAvailable() {				
		boiler.setAvailability(null, false);
		assertFalse(boiler.getCupOfWater().isSuccess());
                boiler.getCupOfWater().ifFailure(message->assertEquals(message, Boiler3.WATER_NOT_AVAILABLE));

		assertFalse(boiler.getCupOfBoiledWater().isSuccess());
                boiler.getCupOfBoiledWater().ifFailure(message->assertEquals(message, Boiler3.BOTH_NOT_AVAILABLE));
	}
	
	
	@Test
	public void testPowerAvailable() {						
		boiler.setAvailability(null, true);
		assertFalse(boiler.getCupOfWater().isSuccess());
                boiler.getCupOfWater().ifFailure(message->assertEquals(message, Boiler3.WATER_NOT_AVAILABLE));

		assertFalse(boiler.getCupOfBoiledWater().isSuccess());
                boiler.getCupOfBoiledWater().ifFailure(message->assertEquals(message, Boiler3.WATER_NOT_AVAILABLE));
	}
	
	@Test
	public void testWaterAvailable() {		
		boiler.setAvailability(new CupOfWater(), false);
		assertTrue(boiler.getCupOfWater().isSuccess());
		assertFalse(boiler.getCupOfBoiledWater().isSuccess());
                boiler.getCupOfBoiledWater().ifFailure(message->assertEquals(message, Boiler3.POWER_NOT_AVAILABLE));
	}
	
	@Test
	public void testBothAvailable() {		
		boiler.setAvailability(new CupOfWater(), true);
		assertTrue(boiler.getCupOfWater().isSuccess());
		assertTrue(boiler.getCupOfBoiledWater().isSuccess());
	}
}

 

And here is the implementation of a boiler class:

public class Boiler3 implements IBoiler3 {

	public static final String WATER_NOT_AVAILABLE = "Water not available.";
	public static final String POWER_NOT_AVAILABLE = "Power not available.";
	public static final String BOTH_NOT_AVAILABLE = WATER_NOT_AVAILABLE + " " + POWER_NOT_AVAILABLE;

	@Nullable
	private CupOfWater water;
	private boolean powerAvailable;
	

	@Override
	public void setAvailability(@Nullable CupOfWater water, boolean powerAvailable) {
		this.water = water;
		this.powerAvailable = powerAvailable;
	}

	@Override
	public Result<CupOfWater, String> getCupOfWater() {
		return water == null
			? Result.failure(WATER_NOT_AVAILABLE)
			: Result.success(water);
	}

	@Override
	public Result<CupOfBoiledWater, String> getCupOfBoiledWater() {
		Result<CupOfWater, String> resultStep1 = getCupOfWater();
		return resultStep1.isSuccess()
				? powerAvailable
					? Result.success(resultStep1.getSuccess().boil())
					: Result.failure(POWER_NOT_AVAILABLE)
				: powerAvailable
					? Result.failure(WATER_NOT_AVAILABLE)
					: Result.failure(BOTH_NOT_AVAILABLE);
	}
}

Note the setting of the values in the getCupOfWater() method, depending on what the method should return.
A simple method for processing the result is shown in the third line of the getCupOfBoiledWater() method. First we analyse the return value with resultStep1.isSuccess(). And then, depending on the answer, we continue processing.
The test demonstrated a more functional way of processing using the ifFailure method:
boiler.getCupOfWater().ifFailure(message->assertEquals(message, Boiler3.WATER_NOT_AVAILABLE));
The method will only be called if the result of the processing was wrong. In this case, the error information (in this case the message) will be automatically provided to your handler (consumer).
As you can see, everything is very simple and elegant.
Well, in the end – the source code of the class itself:

public abstract class Result<SUCCESS, FAILURE> {
	
	public abstract boolean isSuccess() ;
	
	public abstract SUCCESS getSuccess();
	
	public abstract FAILURE getFailure();
	
	public abstract Result<SUCCESS, FAILURE> ifSuccess(Consumer consumerSuccess);
	
	public abstract Result<SUCCESS, FAILURE> ifFailure(Consumer consumerFailure);
	
	public  static class Success<SUCCESS, FAILURE> extends Result<SUCCESS, FAILURE>{
		
		private final SUCCESS _success;
		
		private Success(SUCCESS success) {
			_success = success;
		}

		@Override
		public boolean isSuccess() {
			return true;
		}

		@Override
		public SUCCESS getSuccess() {
			return _success;
		}

		@Override
		public FAILURE getFailure() {
			throw new IllegalStateException("getFailure called on Success");
		}

		@Override
		public String toString() {
			return "Success [_success=" + _success + "]";
		}

		@Override
		public Result<SUCCESS, FAILURE> ifSuccess(Consumer consumerSuccess) {
			consumerSuccess.accept(_success);
			return this;
		}

		@Override
		public Result<SUCCESS, FAILURE> ifFailure(Consumer consumerFailure) {
			return this;
		}
		
	}
	
	public  static class Failure<SUCCESS, FAILURE> extends Result<SUCCESS, FAILURE>{
		
		private final FAILURE _failure;
		private Failure(FAILURE failure) {
			_failure = failure;
		}
		
		@Override
		public boolean isSuccess() {
			return false;
		}

		@Override
		public SUCCESS getSuccess() {
			throw new IllegalStateException("getSuccess called on Failure");
		}

		@Override
		public FAILURE getFailure() {
			return _failure;
		}

		@Override
		public String toString() {
			return "Failure [_failure=" + _failure + "]";
		}

		@Override
		public Result<SUCCESS, FAILURE> ifSuccess(Consumer consumerSuccess) {
			return this;
		}

		@Override
		public Result<SUCCESS, FAILURE> ifFailure(Consumer consumerFailure) {
			consumerFailure.accept(_failure);
			return this;
		}
		
	}
	
	public static <SUCCESS, FAILURE> Result<SUCCESS, FAILURE> failure(FAILURE failure){ return new Failure<>(failure);}
	
	public static <SUCCESS, FAILURE> Result<SUCCESS, FAILURE> success(SUCCESS success){ return new Success<>(success);}
	
}

As in the case of past examples, you will find the source code on:
https://github.com/vsirotin/Smartenesse-Java
In one of the following post, I’ll try to explain how the class Result<SUCCESS, FAILURE> can help you to forget about the nightmare of exceptions in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *