Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

NetBeans in the Classroom: Time to Say Goodbye to NULL as a Return Value

DZone's Guide to

NetBeans in the Classroom: Time to Say Goodbye to NULL as a Return Value

· Java Zone
Free Resource

Build vs Buy a Data Quality Solution: Which is Best for You? Gain insights on a hybrid approach. Download white paper now!

Ken Fogel is the Program Coordinator and Chairperson of the Computer Science Technology program at Dawson College in Montreal, Canada. He is also a Program Consultant to and part-time instructor in the Computer Institute of Concordia University's School of Extended Learning. He blogs at omniprogrammer.com and tweets @omniprof.

I loved null. When I taught C and then C++, I explained to my students how pointers had three states. The first state was that of a valid address to an object in memory. The second was an invalid address or garbage address that occurs when a local pointer is created or a pointer goes out of scope. Unfortunately there was no way to tell a valid from an invalid address. Then there was the null state. This is a pointer in waiting. Not yet pointing at a valid object and easily recognized as such. I required my students to initialize local pointer references to null or to assign them a null after deleting them. Every time a function returned a pointer the very next line of code after calling the function had to read “if (ptr == null)”.

When I moved on to teaching Java, the pointer was gone. Any student who sees their first null pointer error message may disagree but I leave that to another debate. In place of the pointer is the reference. Like the C/C++ pointer, it too has three states. The major Java enhancement is that the compiler will recognize that you are trying to use the invalid address state on the left hand side of an expression and will declare an error.

So that leaves us with a valid address or null. Therefore, just like with C/C++, a programmer must test if a reference is null or not. This must never be ignored. It just takes one Java method that attempts to access a value in, as an example, an ArrayList whose reference is null to bring down a program.

To see the problem, let's look at this method:

public ArrayList findAll() throws SQLException {
        ArrayList rows = null;
        String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH";
        try (Connection connection = DriverManager.getConnection(url, user,
                password);
                PreparedStatement pStatement = connection
                .prepareStatement(selectQuery);
                ResultSet resultSet = pStatement.executeQuery()) {
            if (resultSet.next()) {
                // Only create the ArrayList if there is something to put in it
                rows = new ArrayList<>();
                do {
                    FishData fishData = new FishData();
                    fishData.setCommonName(resultSet.getString("COMMONNAME"));
                    fishData.setDiet(resultSet.getString("DIET"));
                    fishData.setKh(resultSet.getString("KH"));
                    fishData.setLatin(resultSet.getString("LATIN"));
                    fishData.setPh(resultSet.getString("PH"));
                    fishData.setFishSize(resultSet.getString("FISHSIZE"));
                    fishData.setSpeciesOrigin(resultSet.getString("SPECIESORIGIN"));
                    fishData.setStocking(resultSet.getString("STOCKING"));
                    fishData.setTankSize(resultSet.getString("TANKSIZE"));
                    fishData.setTemp(resultSet.getString("TEMP"));
                    fishData.setId(resultSet.getLong("ID"));
                    rows.add(fishData);
                } while (resultSet.next());
            }
        }
        return rows;
    }

The way in which this method is coded, the ArrayList is only created if there were any records returned from the database query otherwise a null is returned. Here is a method that converts the ArrayList of records into a String by calling upon findAll():

    public String retrieveFish() {
        StringBuilder sb = new StringBuilder();
        FishDAO fishDAO = new FishDAOImpl();
        ArrayList data;
        try {
            data = fishDAO.findAll();
            // Must check for null
            if (data != null) {
                data.stream().forEach((fd) -> {
                    sb.append(fd.toString()).append("\n");
                });
            }
        } catch (SQLException e) {
            log.error("Error retrieving records: ", e.getCause());
        }
        return sb.toString();
    }

There is nothing wrong with this code. What can go wrong is the programmer, especially a student programmer. The error is forgetting the if (data != null). Here is a classic student error:

        try {
            // Oops, forgot to check for null
            data = fishDAO.findAll();
            data.stream().forEach((fd) -> {
                sb.append(fd.toString()).append("\n");
            });
        } catch (SQLException e) {
            log.error("Error retrieving records: ", e.getCause());
        }

If only there was some way to make a programmer aware of the possibility of a null reference? There is and it appeared in Java 8. It is the Optional type. Its primary purpose is to eliminate returning null from methods. By changing the return type to Optional we can change the findAll() method’s first and last lines:

public Optional> findAll() throws SQLException {
	. . . 
	// The original code does not change
	. . .
        return Optional.ofNullable(rows);
}

If the reference being returned is placed in an Optional.ofNullable() then it may or may not be null. The caller of this method must deal with the possibility of a null.

Here is how it is dealt with:

    public Optional retrieveFish() {
        StringBuilder sb = new StringBuilder();
        FishDAO fishDAO = new FishDAOImpl();
        //ArrayList data;
        try {
            // ifPresent is required so a check for null can never be forgotten 
            fishDAO.findAll().ifPresent(data -> data.stream().forEach((fd) -> {
                sb.append(fd.toString()).append("\n");
            })
            );
        } catch (SQLException e) {
            log.error("Error retrieving records: ", e.getCause());
        }
        return Optional.of(sb.toString());
    }

Notice that to access the ArrayList in the Optional the method ifPresent() is used. If this is true then the lambda is invoked to copy the ArrayList into the StringBuilder object.

Another feature of Optional is in play in this method. The return type is Optional<String> and the last statement reads return Optional.of(sb.toString()). The of() member method of Optional can only accept a non-null value so if the StringBuilder object is somehow null there will be an exception.

The next time you are writing a method that can return a null reference please don’t. Return an Optional instead.

Build vs Buy a Data Quality Solution: Which is Best for You? Maintaining high quality data is essential for operational efficiency, meaningful analytics and good long-term customer relationships. But, when dealing with multiple sources of data, data quality becomes complex, so you need to know when you should build a custom data quality tools effort over canned solutions. Download our whitepaper for more insights into a hybrid approach.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}