Java 8 Optional and objects with dynamic structure. Part 2


Image source: PIRO4D pixabay.com

This second post in the series about Optional in Java 8. The first post you’ll find here:

Java 8 Optional and objects with dynamic structure. Part 1

In the first post of this series, I tried to consider approaches that you can use to implement objects with a dynamic structure and promised to justify why almost always Optional in this situation works better than other approaches.

Before we move on to the specific use case, we try to answer the question: what is Optional <T>?

I’ll venture to give my own visual definition: in the first approximation Optional <T> is an analog of the case of a physical object, for example – glasses. Whether the object is inside the case, you can learn with the isPresent () method. If it is there, you can take it with the get () method.

Well, to get to the next level of understanding, read the documentation 🙂

In this post I tried to show the elegance of this mechanism in a small example. The source texts of the example considered in this post are found on GitHub:

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

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

His scheme is shown in the picture below:

As you can see, the boiler requires water and electricity. At the outlet, the boiler can produce cold or boiled water.

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

public interface IBoilerInput {
   
   void setAvailability(boolean waterAvailable, boolean powerAvailable);
}

And the output like this:

public interface IBoilerOutput {
   
   Optional<CupOfWater> getCupOfWater();
   
   Optional<CupOfBoiledWater> getCupOfBoiledWater();

}

It is interesting to note here that the device depending on the input data can (or does not) produce cold and boiled water, we present the result of calling get … with Optional <T>.
The behavior of the device as a whole describes an interface that combines input and output methods.

public interface IBoiler extends IBoilerInput, IBoilerOutput {}

Classes representing types of water for us are of little interest, so we implement them in the least possible way. Here is a class for cold water:

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

We will assume that a portion of boiled water is a new, different from cold water object. Therefore, we will represent it as an independent class:

public class CupOfBoiledWater {}

So, the task is defined. In the best traditions of TDD (Test-Driven Development), we first write a test to check if we have correctly simulated the behavior of our simple device:

public class Boiler1Test {
   
   private IBoiler boiler;
   
   @Before
   public void setUp() throws Exception {
      boiler = new Boiler1();
   }

   @Test
   public void testBothNotAvailable() {            
      boiler.setAvailability(false, false);
      assertFalse(boiler.getCupOfWater().isPresent());
      assertFalse(boiler.getCupOfBoiledWater().isPresent());
   }
   
   
   @Test
   public void testPowerAvailable() {                
      boiler.setAvailability(false, true);
      assertFalse(boiler.getCupOfWater().isPresent());
      assertFalse(boiler.getCupOfBoiledWater().isPresent());
   }
   
   @Test
   public void testWaterAvailable() {    
      boiler.setAvailability(true, false);
      assertTrue(boiler.getCupOfWater().isPresent());
      assertFalse(boiler.getCupOfBoiledWater().isPresent());
   }
   
   @Test
   public void testBothAvailable() {     
      boiler.setAvailability(true, true);
      assertTrue(boiler.getCupOfWater().isPresent());
      assertTrue(boiler.getCupOfBoiledWater().isPresent());
   }
}

As you can see, our test verifies that the device does indeed issue cold water if water is supplied to the input, regardless of the presence of electricity. But the boiled water is given by the device only if there is water and power supply.
Before proceeding to implementation, stop for a minute and think how would you program this task in the approaches discussed in the previous post:

  • Using the pair has … get …
  • By using Exception from inside get when object is unavailable
  • Using the sign of the activity of the outward object.
    If you really tried to imagine it, or better yet, try to program a solution within these approaches, you will undoubtedly appreciate the simplicity and elegance that Java 8 Optional brings to our programming life.

Look at my, probably not the best solution:

public class Boiler1 implements IBoiler {
   
   private boolean waterAvailable;
   private boolean powerAvailable;
   

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

   @Override
   public Optional<CupOfWater> getCupOfWater() {
      return waterAvailable ? Optional.of(new CupOfWater()) : Optional.empty();
   }

   @Override
   public Optional<CupOfBoiledWater> getCupOfBoiledWater() {
      if(!powerAvailable)return Optional.empty();
      return getCupOfWater().map(cupOfWater->cupOfWater.boil());
   }
}

Pay attention to the last significant line of the listing, where the map () method is used from the Optional class. So you can build a chain of processing. If on one of the links in the chain it turns out that further processing is impossible, the entire chain will return an empty response.

Well, that’s all for today.

In the next post we will consider the remaining topics:

  • Using Optional in special situations of processing objects (Navigation e.g. for Persistence or Validation)
  • Optional: when do you use it?
  • Optional: is it part of the functional programming?