Friday, July 15, 2011

The Value of String.valueOf

Most Java developers have probably had their fill of NullPointerException. Most of us have learned the value of doing certain things to reduce our "opportunities" of encountering the NullPointerException. Indeed, there is a Wiki page dedicated to preventing or reducing NullPointerExceptions.

Several people have argued for additional language support for improved and easier handling of potential null. These include Java SE 7 proposals, Optimized Null Check, and Kinga Dobolyi's thesis Changing Java’s Semantics for Handling Null Pointer Exceptions.

Among the many things we can already do rather easily to reduce our encounters with NullPointerException, one particular easy thing to do is to apply String.valueOf(Object) when appropriate. The String.valueOf(Object) method, as its Javadoc-generated documentation states, returns "null" if the passed in object is null and returns the results on the passed-in Object's toString() call if the passed-in Object is not null. In other words, String.valueOf(String) does the null checking for you.

The use of String.valueOf(Object) is particularly useful when implementing toString methods on custom classes. Because most toString implementations provide the class's data members in String format, String.valueOf(Object) is a natural fit. All Java objects based on classes that extend Object provide a toString() implementation even if it is simply their parent's (or even Object's) implementation of toString(). However, if a member class implements toString but the member itself is null rather than an instance of the class, then the toString() does no good (and actually leads to a NullPointerException when called).

This is demonstrated with the following example code.

Consider a class called PersonName, which holds last name and first name of the person:

package com.vaani.string;

/**
* Class upon which to call toString.
*/
public class PersonName
{
private String lastName;
private String firstName;

public PersonName(final String newLastName, final String newFirstName)
{
lastName = newLastName;
firstName = newFirstName;
}

/**
* Provide String representation of me.
*
* @return My String representation.
*/
@Override
public String toString()
{
return firstName + " " + lastName;
}
}


Person.java


package com.vaani.string;



public class Person
{
private PersonName name;

public Person( PersonName newName)
{
name = newName;
}



/**
* Provide String representation of me.
*
* @return My String representation.
*/
public String toString()
{
//to be implemented
}
}


Now this person.java has toString() which is to be implemented, but we will see how it throws NPE. Consider the main method:


PersonName personName = new PersonName("Flintstone", null);
//print personname as string
System.out.println("Person's Name [DIRECT]: " + personName);
System.out.println("Person's Name [TOSTRING]: " + personName.toString());
System.out.println("Person's Name [STRING.VALUEOF]: " + String.valueOf(personName));
Output will be:

Person's Name         [DIRECT]: null Flintstone
Person'
s Name [TOSTRING]: null Flintstone
Person's Name [STRING.VALUEOF]: null Flintstone

Now consider the toString() func is represented as

Also if we call Person's constructor with personName(instance created above), everything will still be fine:

PersonName name;
//code skipped
public String toString()
{
// Don't use -- can lead to runtime error (NullPointerException)
return name.toString();
}

Now as we did earlier:

PersonName personName = new PersonName("Flintstone", null);

//create next object from personName
Person personOne = new Person(personName);
System.out.println("Person One [DIRECT]: " + personOne);
System.out.println("Person One [TOSTRING]: " + personOne.toString());
System.out.println("Person One [STRING.VALUEOF]: " + String.valueOf(personOne));

This will work fine because of personName will return its toString() func, and dont throw any NPE. But this will result in bang:

Person personTwo = new Person(null);
System.out.println("Person Two [DIRECT]: " + personTwo);
System.out.println("Person Two [TOSTRING]: " + personTwo.toString());
System.out.println("Person Two [STRING.VALUEOF]: " + String.valueOf(personTwo));
Each of this above line throws exception. So please be aware of this. To avoid null pointer exception you can implement toString() func of Person like this:

public String toString()
{
// It's all good
return String.valueOf(name);
}
Now the personTwo will be printed as null.
Full code listing for Person.java(personName.java is already covered):

package com.vaani.string;



public class Person
{
private PersonName name;

public Person( PersonName newName)
{
name = newName;
}



/**
* Provide String representation of me.
*
* @return My String representation.
*/
public String toString()
{
// Don't use -- leads to compiler time error (incompatible types)
//return name;

// Don't use -- can lead to runtime error (NullPointerException)
//return name.toString();

// It's all good
return String.valueOf(name);
}
}

main method to run this code:

package com.vaani.string;

import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Logger;

/**
* Example class demonstrating use of String representations available through
* implicit String, toString(), and String.valueOf().
*/
public class StringValueFunc
{
private static final String NEW_LINE = System.getProperty("line.separator");

/** Using java.util.logging. */
private static Logger LOGGER = Logger.getLogger(
StringValueFunc.class.getName());

/**
* Main function for running tests/demonstrations.
*
* @param arguments Command-line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
//takes last name and first name
PersonName personName = new PersonName("Flintstone", null);

//create next object from personName
Person personOne = new Person(personName);
//just initiate to null
Person personTwo = new Person(null);
printHeader("String representation of direct Strings", System.out);

System.out.println("Person's Name [DIRECT]: " + personName);
System.out.println("Person's Name [TOSTRING]: " + personName.toString());
System.out.println("Person's Name [STRING.VALUEOF]: " + String.valueOf(personName));
printBlankLine(System.out);

printHeader("String representation of non-null complex object", System.out);

System.out.println("Person One [DIRECT]: " + personOne);
System.out.println("Person One [TOSTRING]: " + personOne.toString());
System.out.println("Person One [STRING.VALUEOF]: " + String.valueOf(personOne));
printBlankLine(System.out);

printHeader("String representation of null complex object", System.out);

System.out.println("Person Two [DIRECT]: " + personTwo);
System.out.println("Person Two [TOSTRING]: " + personTwo.toString());
System.out.println("Person Two [STRING.VALUEOF]: " + String.valueOf(personTwo));
printBlankLine(System.out);
}

public static void printHeader(final String message, final OutputStream out)
{
final String headerSeparator =
"====================================================================";

try
{
out.write((headerSeparator + NEW_LINE + message + NEW_LINE).getBytes());
out.write((headerSeparator + NEW_LINE).getBytes());
}
catch (IOException ioEx)
{
System.out.println(headerSeparator);
System.out.println(message);
System.out.println(headerSeparator);
LOGGER.warning("Could not write header information to provided OutputStream.");
}
}

public static void printBlankLine(final OutputStream out)
{
try
{
out.write(NEW_LINE.getBytes());
}
catch (IOException ioEx)
{
System.out.println(NEW_LINE);
LOGGER.warning("Could not write blank line to provided OutputStream.");
}
}


}

The above code can be used to demonstrate building of a toString method on a complex object and how its behaves when called by an owning class. The method of most interest is at the bottom of the code shown above. Two return values are commented out because of problems associated with them. The final example, using String.valueOf(Object) is NOT commented out because it works the best each time it is run whether or not the complex PersonName object is null. The next three images show the output for each of these presentations of the Person objects' String representations.











Finally, the String class provides many overloaded valueOf methods. In addition to the version that was the focus of this blog post (accepts an Object), the other overloaded versions of valueOf accept primitive data types and arrays of primitive data types.
Conclusion
Regardless of what the future brings in terms of improved null handling in Java, there are many tactics we can take today to reduce the unwanted (sometimes we actually do want them thrown!) occurrences of NullPointerException. One of these is to use String.valueOf(Object) when appropriate.

Additional Resources

No comments:

Post a Comment

Chitika