📜 ⬆️ ⬇️

Object in a case or Optional in Java 8 and Java 9. Part 1: “How to live without it?”

Object in the case

Perhaps you have already had to deal with objects that change their structure according to the laws of their internal development or depending on external conditions. In practice, this means that parts of an object for the outside world can sometimes be available, and sometimes not.
What kind of objects can it be? Many service companies, commercial enterprises and banks provide various types of services depending on the time of day. Different devices may or may not perform certain actions depending on their state. If you read this text from the screen of a smartphone or laptop, it may stop showing it if the battery charge drops below a certain limit.
In this and subsequent articles of this tutorial, we will look at simple objects with dynamic structure, methods of modeling them and using them in Java using the methods of the Optional class.
Much has been written about the use of the methods of this class, including in Russian, including on Habré. In this series of articles, I will try to look at the problem more broadly, to consider the advantages and disadvantages of the chosen solution to the problem, as well as alternative approaches.
In this first article in the series, we’ll talk about how you can do without NullPointerException without using Optional in Java. I hope you will see that you can live without an Optional, but life should become more pleasant with it. The second article in this series describes the methods of the Optional class as it appeared in Java 8, and the third describes the methods of the class added in Java 9. The fourth article deals with the necessary (from the author’s point of view) addition to this class. Well, in the fifth article I tell about where the Optional should be used inside the class, summarize and give every reader who has read the series to the end, a valuable gift.

So, let's discuss how to use Java to access elements with a dynamic structure, for example, our first device: a simple coffee machine.
It should immediately make a reservation that the physical structure of the device does not change with time. Speaking about the dynamics of the structure, we look at the device through the user's eyes. If for some reason the device cannot make coffee to the user, then this functionality for the user is practically not present at the moment.
How can I express this with Java?
Suppose we were asked to write a control unit for such an instrument and we need a function like
device.getCoffeePortion() 

What type of item should this method return? After all, sometimes the device may be able to give the user coffee, and sometimes not.
There are not many ways to solve this problem (at least known to me).

We return either object or null


The most common solution is to return some object in a positive case and null in a negative one. The user must therefore always check the return value and use it only if it is not null.
The problem is that the user of your object must somehow know or guess about the possibility of returning a zero object. A programmer who calls your method in his code can pass it as a parameter in a chain of calls to other functions, check the list, and so on. This means that payment in the form of a NullPointerException can come much later and in a substantially remote function call place
NullPointerException is a notorious champ among Java programmer errors, both in number and in terms of time and nerves spent on finding and eliminating them.
It is interesting to note that this catastrophe has an author who called it his “billion-dollar mistake”. Null appeared much earlier than Java in 1965 when developing the ALGOL language.
After 44 years, Tony Hoare, the creator of this monster (but also a wonderful scientist) wrote in his article “Null References: The Billion Dollar Mistake” (QCon, August 25, 2009) ,
I’m calling you a ballman for the compiler. But I couldn’t have been able to implement it. This has led to innumerable errors, health problems, and system crashes.

As an object creator, you can somewhat mitigate the situation using an annotated return result, for example:
 @Nullabble CoffeePortion getCoffeePortion() 

If a programmer uses a modern IDE, then in certain situations it will tell him to check for a null object. But this does not always happen and you should not count on it especially.
So, returning null as the result of an operation is bad. So this should be avoided. But as?
')

First ask well


One solution is to supply each get ... method with a partner has ... The object user must somehow be warned that before the get request ... he must ask if there is a value to return. In our case, the pair would look like this:
 boolean hasCofeePortion() CoffeePortion getCoffeePortion() 

If the first function returns true, it thus gives the go-ahead to the function of the steamer function. The user can theoretically try not to call the first has ... function. If the value at the time of the call is not available, the function get ... returns not null, as it was in the first variant, but throws Exception.
Thus, the problem of transferring a “dangerous” object along the call chain is eliminated. This is the advantage of the approach. But the approach has two significant drawbacks. First, the number of functions is multiplied. Secondly, there is no guarantee that at the time of the has ... call, the object was available, and at the time of get ... it was no longer available. (In our simple example, this is impossible, but in complex systems, where the state of objects depends on time or external influences, and other operations are required to prepare the get ... call, this danger is real.
So, first ask, and then take - not a good strategy. Can't the other way around? It is this idea that uses the following approach.

The object is always issued, but not always alive.


In this approach, the object is always returned, but contains an additional feature. This sign tells us, roughly speaking, whether it is in fact or not. Sharpening to the extreme, you can formulate this approach as follows: you get either a living object or its corpse. But even if it is a corpse, he speaks a little.
Namely, the approach says that you can always call a method like isActive () or isPresent (). Even the “dead” object. And with a positive response to work with the object further.
An obvious disadvantage of this approach is the hanging on the object of a technical feature, which cannot be explained from the point of view of its own functioning (business logic). In certain situations, this feature may conflict with this logic. For example, imagine that an object is associated with two other objects. From the point of view of one object, it can be active or “alive”, and from the point of view of another “dead”.
However, from a technical point of view, this approach may have its own, sometimes unexpected, advantages. For example, if we need to save or forward a compound object as JSON or XML, it is wise to send it completely, including its currently deactivated parts. After all, it may be that after restoring an object (reading from a file or stream), the deactivated parts sometime have to be activated.
So, this approach is good because it does not lead to a NullPointerException, but is “unnatural”. Is it possible to find something similar, but without the shortcomings described?

Sheet returned, possibly empty


One of the most recommended approaches by experts before the Java 8 Optional approach was the approach described below.
His idea is extremely simple. Instead of an object, we return an array or a list of objects. This array or list can either be empty or contain exactly one object.
In fact, we are thus forcing the users of our method on the spot, immediately after calling the function, deal with the list. It is unlikely that he will pass on this list further as a parameter of other methods.
The disadvantage of the method is its obvious pretentiousness. The situations when the get ... method returns an elementary value, for example int, a known object and a conditionally existing object, start to differ very much.
And in order to overcome this contradiction, as well as for other reasons, the developers of the Java language decided to introduce Optional. We will talk about the advantages and disadvantages of this class in the next articles in this series.

Go to the next article.

Illustration: ThePixelman

Source: https://habr.com/ru/post/347480/


All Articles