The book "Modern Java. Lambda Expressions, Streams, and Functional Programming ”

image Hello, habrozhiteli! The advantage of modern applications is in advanced solutions, including microservices, reactive architectures and streaming data processing. Lambda expressions, data streams and the long-awaited system of modules of the Java platform greatly simplify their implementation.



The book will help you master the new features of modern add-ons, such as the Streams API and the Java platform module system. Discover new approaches to competitiveness and learn how concepts of functionality improve the work with code.



In this book: • New Java features • Streaming data and reactive programming • Java platform module system.



Excerpt. Chapter 11. Optional class as the best alternative to null



Raise your hand if during your career as a Java developer you have ever received a NullPointerException. Leave it raised if this exception is the most common one you have encountered. Unfortunately, we do not see you now, but it is very likely that your hand is raised. We also suspect that you may be thinking something like: “Yes, I agree. Exceptions NullPointerException is a headache for any Java developer, whether it is a beginner or an expert. But nothing can be done with them anyway, this is the price we pay for using such a convenient and probably inevitable design as empty links. ” This is a common opinion in the world of (imperative) programming; nevertheless, perhaps this is not the whole truth, but rather a deeply rooted prejudice.



The British computer science specialist Tony Hoare, who created empty links back in 1965, while developing the ALGOL W language, one of the first typed programming languages ​​with records that were allocated on the heap, later admitted that he did this “ just because of the ease of implementation. ” Although he wanted to guarantee “complete safety of use, an exception for empty links, because he thought that this was the most convenient way to simulate the absence of a value. Many years later, he regretted this decision, calling it "my billion dollar mistake." We all saw the results of this decision. For example, we can check the field of an object to determine whether it represents one of two possible values, only to find that we are checking not an object, but a null pointer, and immediately get this annoying NullPointerException.



In fact, Hoar may have underestimated the sheer cost of fixing millions of developers of errors caused by empty links over the past 50 years. Indeed, the vast majority of programming languages1 created over the past decades, including Java, are based on the same design decision, perhaps for reasons of compatibility with older languages ​​or (more likely), as Hoar said, “simply because of the ease of implementation ". We'll start by demonstrating a simple example of the problems encountered when using null.



11.1. How to simulate a lack of value



Imagine that you have the nested object structure in Listing 11.1 for the owner of a car that bought a car insurance.



Listing 11.1. Person / Car / Insurance Data Model



public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
      
      





What do you think is the problem with the following code?



 public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
      
      





This code looks quite reasonable, but many people don’t have cars, so what is the result of calling the getCar method in this case? Often (and in vain) they return an empty link to indicate the absence of a value (in this case, to indicate the absence of a machine). As a result, calling the getInsurance method will return blank link insurance, which will cause a NullPointerException to be thrown at runtime and the program will stop. But that's not all. But what if the person object was null? What if getInsurance also returned null?



11.1.1. Reduce NullPointerException with Security Check



How to avoid unexpected NullPointerException? You can usually add null checks wherever you need (and sometimes, exceeding the requirements of safe programming, and where you do not need to), and often in different styles. Our first attempt to write a method to prevent the generation of a NullPointerException is shown in Listing 11.2.



image






This method checks for null each time a variable is dereferenced, returning the string value “Unknown” if at least one of the variables encountered in this dereferencing chain is an empty value. The only exception to this rule is that we do not check the name of the insurance company for null, because we know that (like any other company) it must have a name. Please note that we manage to avoid this last check only due to knowledge of the subject area, but this fact is not reflected in the Java classes that model our data.



We described the method in Listing 11.2 as “deep doubts,” because a repeating pattern is noticeable in it: each time in doubt, the variable is not null, you have to add another nested if block, thereby increasing the level of code indentation. Obviously, this technique does not scale well and reduces readability, so it’s better to try a different solution. To avoid this problem, let's take a different path, as shown in Listing 11.3.



In the second attempt, we try to avoid deep nesting of if blocks using a different strategy: whenever we stumble upon a null variable, we return the string value “Unknown”. But this solution is also far from ideal; Now the method has four different exit points, which greatly complicates its maintenance. In addition, the default value returned in the case of null - the string "Unknown" - is repeated in three places and (we hope) does not contain spelling errors! To avoid this, you can, of course, move the repeating string to a constant.



image






Moreover, this process is error prone. What if you forget to check if one of the properties is null? In this chapter, we argue that using null to represent a lack of value is a fundamentally wrong approach. A better way of modeling the absence and presence of value is required.



11.1.2. Problems encountered with null



To summarize the above, the use of empty links in Java leads to the following, both theoretical and practical problems.





As a basis for other feasible solutions, in the next subsection we will briefly review the possibilities offered by other programming languages.



11.1.3. Alternatives to null in other programming languages



In recent years, languages ​​such as Groovy have managed to circumvent this problem by introducing a safe call operator (?.), Designed to work safely with values ​​that are potentially null. To understand how this process is implemented in practice, consider the following Groovy code. It retrieves the name of the insurance company in which the specified person insured the car:



def carInsuranceName = person?.car?.insurance?.name







It should be clear to you what this code does. A person may not have a car, for the simulation of which we assign null to the car link of the person object. Similarly, a machine may be uninsured. Groovy’s safe call operator allows you to safely work with potentially empty links without throwing a NullPointerException, passing a blank link down the call chain and returning null if any value from the chain is null.



A similar feature was proposed to be implemented in Java 7, but then it was decided not to do this. However, oddly enough, the safe call operator is not really needed in Java. The first thought of any Java developer when encountering a NullPointerException is to quickly fix the problem by adding an if statement to check for null before calling its method. Solving the problem in a similar way, without considering whether null is acceptable in this particular situation for your algorithm or data model, does not lead to a correction, but to hiding the error. And subsequently, for the next developer (probably yourself in a week or a month), finding and fixing this error will be much more difficult. In fact, you just sweep the trash under the carpet. Groovy's null-safe dereferencing operator is just a larger and more powerful broom that can do such stupid things without worrying about the consequences.



Other functional programming languages, such as Haskell and Scala, look at this problem differently. Haskell has a Maybe type, which essentially encapsulates an optional value. An object of type Maybe may contain a value of the specified type or contain nothing. Haskell lacks the concept of an empty link. In Scala, to encapsulate the presence or absence of a value of type T, a similar logical structure Option [T] is provided, which we will discuss in Chapter 20. It is necessary to explicitly check whether a value is present using operations of type Option, which provides “null checks” . Now it is no longer possible to “forget to check for null”, since the type system itself requires checking.



Okay, we are a little off topic, and it all sounds pretty abstract. You are probably wondering what Java 8 offers in this sense. Inspired by the idea of ​​an optional value, the creators of Java 8 introduced a new class, java.util.Optional! In this chapter, we will show what the advantage of using it is to model potentially missing values ​​instead of assigning them an empty reference. We will also explain why such a transition from null to Optional requires the programmer to review the ideology of working with optional values ​​in the domain model. Finally, we will explore the possibilities of this new Optional class and provide some practical examples of its effective use. As a result, you will learn how to design improved APIs in which the user already understands from the method signature whether an optional value is possible here.



11.2. Introducing the Optional Class



In Java 8, under the influence of the Haskell and Scala languages, a new java.util.Optional class has appeared to encapsulate an optional value. For example, if you know that a person may or may not own a car, then you should not declare the variable car in the Person class with type Car and assign it an empty reference if the person does not have a car; instead, its type should be Optional, as shown in fig. 11.1.



image






Given a value, the Optional class serves as an adapter for it. Conversely, the absence of a value is modeled using the empty optional returned by the Optional.empty method. This static factory method returns a special lone instance of the Optional class. You might be wondering what the difference between an empty link and Optional.empty () is. Semantically, they can be considered one and the same, but in practice there are enormous differences between them. An attempt to dereference null inevitably leads to a NullPointerException, and Optional.empty () is a valid, workable object of type Optional, which can be conveniently accessed. Soon you will see exactly how.



An important practical semantic difference in using Optional objects instead of null: declaring a variable of type Optional clearly says that an empty value is allowed at this point. And vice versa, always using the Car type and, possibly, sometimes assigning empty references to variables of this type, you mean that you rely only on your knowledge of the subject field to understand whether null belongs to the scope of the given variable.



With this in mind, you can redo the original model from Listing 11.1. We use the Optional class, as shown in Listing 11.4.



Note that using the Optional class enriches the semantics of the model. An Optional field in the Person class and an Optional field in the Car class reflect the fact that a person may or may not have a car, just as a machine may or may not be insured.



At the same time, declaring the name of the insurance company as String, rather than Optional, clearly indicates that the insurance company must have a name. Thus, you know for sure that you will get a NullPointerException when dereferencing the name of the insurance company; adding null check is not necessary, because it would only hide the problem. The insurance company should have a name, so if you come across a company without a name, you need to find out what is wrong with the data, and not add a piece of code to hide this fact.



image






The consistent use of optional values ​​creates a clear distinction between a value that may be absent and a value that is missing due to an error in the algorithm or a problem in the data. It is important to note that the Optional class is not intended to replace all empty links with a single one. Its task is to help design more understandable APIs so that by the signature of the method it can be understood whether an optional value can be found there. The Java type system forces an unpack option to handle the absence of a value.



About the authors



Raul-Gabriel Urma is the CEO and co-founder of Cambridge Spark (UK), a leading educational community for data researchers and developers. Raul was elected one of the participants in the Java Champions program in 2017. He has worked for Google, eBay, Oracle, and Goldman Sachs. He defended his thesis in computer technology at Cambridge University. In addition, he holds a master's degree in engineering from Imperial College London, graduated with honors and received several awards for rationalization proposals. Raul has presented more than 100 technical reports at international conferences.



Mario Fusco is a senior software engineer at Red Hat, involved in the development of the Drools kernel, the JBoss rule engine. He has rich development experience in the Java language, participated (often as a leading developer) in many corporate projects in various industries, from the media to the financial sector. Among his interests are functional programming and domain-specific languages. Based on these two hobbies, he created the open source lambdaj library, wanting to develop an internal Java DSL for working with collections and make it possible to use some elements of functional programming in Java.



Alan Mycroft is a professor at the Department of Computer Science at Cambridge University, where he has been teaching since 1984. He is also an employee of Robinson College, one of the founders of the European Association of Programming Languages ​​and Systems, and one of the founders and trustees of the Raspberry Pi Foundation. He has degrees in mathematics (Cambridge) and computer science (Edinburgh). Alan is the author of more than 100 scientific articles. He was the supervisor for over 20 PhD candidates. His research mainly deals with the field of programming languages ​​and their semantics, optimization and implementation. For a time, he worked at AT&T Laboratories and Intel's research and development department, and also co-founded Codemist Ltd., which released the C language compiler for the ARM architecture called Norcroft.



»More details on the book can be found on the publisher’s website

» Contents

» Excerpt



For Habrozhitelami 25% discount on the coupon - Java .



Upon payment of the paper version of the book, an electronic book is sent by e-mail.



All Articles