DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling

Trending

  • Stateless vs Stateful Stream Processing With Kafka Streams and Apache Flink
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • Power BI Embedded Analytics — Part 2: Power BI Embedded Overview
  1. DZone
  2. Coding
  3. Java
  4. No More Excuses to Use Null References in Java 8

No More Excuses to Use Null References in Java 8

By 
Mario Fusco user avatar
Mario Fusco
·
Oct. 08, 12 · Interview
Likes (1)
Comment
Save
Tweet
Share
75.7K Views

Join the DZone community and get the full member experience.

Join For Free
Tony Hoare introduced null references in ALGOL W back in 1965 “simply because it was so easy to implement”. After many years he regretted his decision calling it "my billion dollar mistake".

Unfortunately the vast majority of the languages created in the last decades have been built with the same wrong design decision so language designers and software engineers started to look for workarounds to avoid the infamous NullPointerException.

Functional languages like Haskell or Scala structurally resolve this problem by wrapping the nullable values in an Option/Maybe monad. Other imperative languages like Groovy introduced a null-safe dereferencing operator (?. operator) to safely navigate values that could be potentially null. A similar feature has been proposed (and then discarded) as part of the project Coin in Java 7.

Honestly I don't miss a null safe dereferencing operator in Java even because I can imagine that the majority of developers would start abusing it "just in case". Moreover, since the upcoming Java 8 will have lambda expressions, it will be straightforward to implement an Option monad that, as I hope to show in the remaining part of the post, is a far more powerful and flexible construct. 

I don't want to delve in category theory and explain what a monad is, even because there are already tons of very goodarticlesdoing this. My purpose is to quickly implement an Option monad using the Java 8 lambda expression syntax and then show how to use it with a very practical example. In Scala, a monad M is any class having the following 3 methods:
def map[B](f: A => B): M[B]
def flatMap[B](f: A => M[B]): M[B]
def filter(p: A => Boolean): M[A] 
In particular you can think to an Option monad as a wrapper around a, possibly absent, value. So an Option of a generic type A could be define as it follows:
import java.util.functions.Predicate;
public abstract class Option<A> {

    public static final None NONE = new None();

    public abstract <B> Option<B> map(Func1<A, B> f);

    public abstract <B> Option<B> flatMap(Func1<A, Option<B>> f);

    public abstract Option<A> filter(Predicate<? super A> predicate);

    public abstract A getOrElse(A def);

    public static <A> Some<A> some(A value) {
        return new Some(value);
    }

    public static <A> None<A> none() {
        return NONE;
    }

    public static <A> Option<A> asOption(A value) {
        if (value == null) return none(); else return some(value);
    }
} 
I also added some convenient factory methods for the Some and None concrete implementations of Option that I will implement later. Here Predicate is a single method interface defined in the new java.util.functions package:
public interface Predicate<T> {
    boolean test(T t);
}
that is used to determine if the input object matches a given criteria, while Func1 is another single method interface:
public interface Func1<A1, R> {
    R apply(A1 arg1);
} 

that I defined to represent a more generic function of one argument of type A1 returning a result of type R.

The abstract class Option has then two concrete implementations, one representing the absence of a value (something that we are used to wrongly model with the infamous null reference):
public class None<A> extends Option<A> {

    None() { }

    @Override
    public <B> Option<B> map(Func1<A, B> f) {
        return NONE;
    }

    @Override
    public <B> Option<B> flatMap(Func1<A, Option<B>> f) {
        return NONE;
    }

    @Override
    public Option<A> filter(Predicate<? super A> predicate) {
        return NONE;
    }

    @Override
    public A getOrElse(A def) {
        return def;
    }
}

and the other wrapping an actually existing value:
public class Some<A> extends Option<A> {

    private final A value;

    Some(A value) {
        this.value = value;
    }

    @Override
    public <B> Option<B> map(Func1<A, B> f) {
        return some(f.apply(value));
    }

    @Override
    public <B> Option<B> flatMap(Func1<A, Option<B>> f) {
        return f.apply(value);
    }

    @Override
    public Option<A> filter(Predicate<? super A> predicate) {
        if (predicate.test(value)) return this; else return None.NONE;
    }

    @Override
    public A getOrElse(A def) {
        return value;
    }
} 
Now, to try to put the Option at work with a concrete example, let's suppose we have a Map<String, String> representing a set of named parameters with the corresponding values. We want to develop the method
int readPositiveIntParam(Map<String, String> params, String name) { // TODO ... }

that, if the value associated with a given key is a String representing a positive integer returns that integer, but returns zero in all other case. In other words we want the following test to pass:
@Test
public void testMap() {
    Map<String, String> param = new HashMap<String, String>();
    param.put("a", "5");
    param.put("b", "true");
    param.put("c", "-3");

    // the value of the key "a" is a String representing a positive int so return it
    assertEquals(5, readPositiveIntParam(param, "a"));

    // returns zero since the value of the key "b" is not an int
    assertEquals(0, readPositiveIntParam(param, "b"));

    // returns zero since the value of the key "c" is an int but it is negative
    assertEquals(0, readPositiveIntParam(param, "c"));

    // returns zero since there is no key "d" in the map
    assertEquals(0, readPositiveIntParam(param, "d"));
}

If we couldn't rely on our Option we should accomplish this task with something similar to this: 
int readPositiveIntParam(Map<String, String> params, String name) { 
String value = params.get(name);
if (value == null) return 0;
int i = 0;
try {
i = Integer.parseInt(value);
} catch (NumberFormatException nfe) { }
if (i < 0) return 0;
return i;
}

too many conditional branches and returning points, isn't it? Using the Option monad we can achieve the same result with a single fluent statement:
int readPositiveIntParam(Map<String, String> params, String name) {
    return asOption(params.get(name))
            .flatMap(FunctionUtils::stringToInt)
            .filter(i -> i > 0)
            .getOrElse(0);
}
where we used an helper static method FunctionUtils.stringToInt() as a function literal, with the :: syntax also introduced in Java 8, defined as:
import static Option.*;
public class FunctionUtils {
    public static Option<Integer> stringToInt(String s) {
        try {
            return some(Integer.parseInt(s));
        } catch (NumberFormatException nfe) {
            return none();
        }
    }
}

This methods tries to convert a String in an int and, if it can't, it returns the None Option. Note that we could also define this behavior inline, while invoking the flatMap() method, using an anonymous lambda expression, but my advice is to develop a small library of utility functions, as I started doing here, in order to leverage the grater reusability allowed by functional programming. I think the comparison of the two readPositiveIntParam methods I provided illustrates well how the extensive use of the Option monad can finally allow us to write completely NullPointerException free software and, more in general, how a bigger employment of functional programming can dramatically reduce its cyclomatic complexity.
Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Designing a Java Connector for Software Integrations
  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!