Convert Object to String (Java)

From LiteratePrograms
Jump to: navigation, search

This program is under development.
Please help to debug it. When debugging
is complete, remove the {{develop}} tag.


Because of a neat compiler trick, any Java reference type or primitive that appears where a String is expected will be converted into a string.

For an reference type (object), this is done by inserting code to call String toString() on the reference. All reference types inherit this method from java.lang.Object, and most (if not all) classes provided in the standard Java Application Programmer's Interface (API) override this method to produce a string that represents the data in a form suitable for printing. To provide this service for Java's primitive types, the compiler must wrap the type in a so-called wrapper class, and call the wrapper class's toString method.

Other classes, for example those that you write yourself, will inherit the standard Object.toString() which produces something like "ClassName@123456" (where the number is the hashcode representation of the object). Thus even user-defined classes will print something! If you want something friendlier, you have to provide a method with the signature public String toString(). But once you have done so, you get the "print friendly" behaviour of the built-in Java classes for free.

This article will present more detail on Java's built-in string conversion methods with examples of converting and concatenating literal strings, creating strings from the reference types provided in the standard Java APIs, and the use of the wrapper classes to print the primitive types. In doing so we will present a description of the Object.toString method which is responsible for most of the magic that the Java compiler performs to convert things to strings. The article concludes with a description of a user defined type's default printing behaviour and its behaviour after introduction of a custom toString method. It is hoped that the article will illustrate some of the background to Java's automated string coercion. It also illustrates some of the extra complexity that a language which makes a distinction between object types and primitives creates for itself.

Contents

[edit] The Automatic Conversion of Objects to String in String Literals

The developers of the Java language recognized that the manipulation of strings would be important to the developers of Java programs. They thus wanted to make the representation and manipulation of strings as straightforward and transparent as possible. They provided language support for the representation of strings as literals; compiler support for the automatic transparent conversion of string literals into String objects; and the automatic conversion from any reference type (that is any subclass of class Object which is more informally called an object) into a printable string by use of the Object.toString method. Despite this extra syntactical support for string literals and automatic object-to-string conversion, String literals and literal expressions are still represented and manipulated internally by the Java run-time system as String objects.

We shall demonstrate this behaviour by means of a simple example.

[edit] 1 + 2 = 3

Consider the print statements:

<<automatic call to toString>>=
System.out.println("Proof that 1 + 2 = 3");
System.out.println("1 + 2 = " + (1 + 2));

When you run this code it prints to the console:

 Proof that 1 + 2 = 3
 1 + 2 = 3

which, despite Java's wordy print statement System.out.println, is very convenient.

When this code is compiled, the Java compiler notes the signature of the method calls to the print statement is System.out.toString(String arg). It therefore knows that the arguments must be converted to a String. In the first expression, String arg is the string literal "Proof that 1 + 2 = 3" and it is converted to a String object by a call to the String constructor:

<<convert string literal to String object>>=
String literal = new String("Proof that 1 + 2 = 3");

The first part of the second print statement is a string literal "1 + 2 = " and is effectively converted to the java expression:

<<convert string literal to String object>>=
String arg0 = new String("1 + 2 = ");

The next part of the expression is the + operator which in the string context means concatenation. The right-hand argument of the concatenation operator is an expression 1 + 2 which has value equivalent to the expression int arg1 = 1 + 2;. This expression is of type int and must be converted to a String before the concatenation can be performed. To convert int to String we must call upon the services of the wrapper class Integer (discussed further later in this article). The Integer class has a method toString which can convert an int value into a String. Thus the compiler will generate code equivalent to:

<<convert int to String object>>=
String arg1 = new Integer(1 + 2).toString();

Now the expression is reduced to

<<concatenate arguments>>=
String arg = arg0 + arg1;

To actually print the strings, System.out.println will call String.toString() and pass the resulting bytes to the console's output stream:

<<print String objects using toString>>=
System.out.println(literal.toString());
System.out.println(arg.toString());

Putting all of this together, the long way to print a pair of String literals is:

<<print 1 + 2 = 3 the long way>>=
convert string literal to String object
convert int to String object
concatenate arguments
print String objects using toString

It is perhaps important to note that this code is close to the code that the compiler actually creates and that Java virtual machine (JVM) must execute to perform this simple operation (although compiler optimization is able to do some simplifications).

To prove this we use a pair of assertions:

<<proof that 1 + 2 = 3>>=
assert ("Proof that 1 + 2 = 3").equals(literal.toString());
assert ("1 + 2 = " + (1 + 2)).equals(arg.toString());

[edit] In String-world 1 + 2 sometimes equals 12

The observant reader will have noted the use of parenthesis in the expression "1 + 2 = " + (1 + 2) and may be wondering why. If we instead wrote:

<<not the way to do it>>=
System.out.println("Proof that sometimes 1 + 2 does not make 3");
System.out.println("1 + 2 = " + 1 + 2);

the result would be the equivalent of:

<<not the way to do it>>=
System.out.println(new String("Proof that sometimes 1 + 2 does not make 3").toString());
System.out.println((new String("1 + 2 = ").toString()) + (new Integer(1).toString()) + (new Integer(2).toString()));

printing

Proof that sometimes 1 + 2 does not make 3
1 + 2 = 12

rather than the expected

Proof that sometimes 1 + 2 does not make 3
1 + 2 = 3

We can ask the compiler to prove this with another assertion:

<<counter proof that 1 + 2 doesn't always make 3>>=
assert ("1 + 2 = " + 1 + 2).equals("1 + 2 = 12");

The technical reason for this, perhaps, counterintuitive behaviour is that under the standard Java operator precedence rules, the concatenation operator binds tighter than the addition operator. Thus the compiler sees "1 + 2 = " + 1 + 2 as a sequence of string concatenations. We need the parenthesis to force Java to perform addition rather than concatenation on the arguments 1 + 2.

[edit] Program 1

Here is the code for the first example in executable form:

<<StringLiterals.java>>=

public class StringLiterals {

  public static void main (String [] args) {
     System.out.println("Demonstrating that Java's built-in support for string literals\n" +
        "relies on the toString method and string concatenation");

     automatic call to toString

     print 1 + 2 = 3 the long way

     proof that 1 + 2 = 3

     not the way to do it

     counter proof that 1 + 2 doesn't always make 3

  }

}

You should note that the test relies on the assert keyword, which was added to Java in JDK 1.4. To run this code you will need to compile the class StringLiterals using a compiler from JDK 1.4 or later. The code for assertion checking is usually turned off, so to have the assert statement executed, the compiler class must be run using the -ea argument:

javac StringLiterals.java
java -ea StringLiterals

We will use the assert keyword in the other programs in this article, and they all must be compiled and run in the same way.

[edit] More about Object.toString()

As demonstrated in the previous section, much of the magic of string literal manipulation is performed behind the scenes by the String toString() method that all Java reference types, or objects, inherit from java.lang.Object. To illustrate this, let's examine the string generated by Object itself.

<<Printing objects>>=
Object o = new Object();
System.out.println("Object printed with toString: " + o);

An easy and elegant way to force an object to become a string is to concatenate it with a prepended empty string literal as in:

<<Printing objects>>=
String objectAsString = "" + o;
System.out.println("Object as a string literal: " + objectAsString);

Of course, because both expressions will eventually call o.toString(), we'd expect the two to be equivalent. We use an assertion to prove that they are:

<<Printing objects>>=
assert objectAsString.equals(o.toString());

A typical run of the program will produce:

 Object printed with toString: java.lang.Object@3e25a5
 Object as a string literal: java.lang.Object@3e25a5

As you can see, the default behaviour of the Object.toString method is to return the fully qualified name of the calling class (in this case java.lang.Object) followed by an hexadecimal code representing the "address" of the object in Java's run-time heap store. The code @3e25a5 that follows the class name will not necessarily be the same when you run the program, or even between program runs.

It is usual for class designers to override the Object.toString method so that classes are able to print useful information about themselves. In the remainder of this section, we shall examine how the designers of the Java API have chosen to override toString for a selection of the most commonly used classes.

[edit] Printing Numbers

In Java, numbers are not objects, but instead, perhaps for performance reasons, are represented by so-called primitive values. The following simple function uses automatic string conversion to produce the table of numbers that often accompanies Java textbooks. The mechanics of how this is done is discussed in the next section.

<<print table of numbers>>=
public static void printTableOfNumbers() {
    String table = "Table of the Java Numbers\n" +
		 "Primitive Type\tSize in Bits\tTypical Value\tMinimum Value\t\tMaximum Value\n" +
		 "==============\t=============\t============\t=============\t\t=============\n";
	// byte
	table += "byte\t\t" + 8 + "\t\t" + (byte)5 + "\t\t" + Byte.MIN_VALUE + "\t\t\t" + Byte.MAX_VALUE + "\n";
	// short
	table += "short\t\t" + 16 + "\t\t" + (short)1024 + "\t\t" + Short.MIN_VALUE + "\t\t\t" + Short.MAX_VALUE + "\n";
	// int
	table += "int\t\t" + 32 + "\t\t" + 65536 + "\t\t" + Integer.MIN_VALUE + "\t\t" + Integer.MAX_VALUE + "\n";
	// long
	table += "long\t\t" + 64 + "\t\t" + 1000000000L + "\t" + Long.MIN_VALUE + "\t" + Long.MAX_VALUE + "\n";
	// float
	table += "float\t\t" + 32 + "\t\t" + 3.14215926 + "\t" + Float.MIN_VALUE + "\t\t\t" + Float.MAX_VALUE + "\n";
	// double
	table += "double\t\t" + 64 + "\t\t" + 100e100 + "\t\t" + Double.MIN_VALUE + "\t\t" + Double.MAX_VALUE + "\n";
	table +=
	     "==============\t=============\t============\t=============\t\t=============\n";

	System.out.println(table);
}

We add this to the code for PrintingObjects as shown here:

<<Printing objects>>=
printTableOfNumbers();

The result should be:

Table of the Java Numbers
Primitive Type  Size in Bits    Typical Value   Minimum Value        Maximum Value
==============  =============   ============    =============        =============
byte            8               5               -128                 127
short           16              1024            -32768               32767
int             32              65536           -2147483648          2147483647
long            64              1000000000      -9223372036854775808 9223372036854775807
float           32              3.14215926      1.4E-45              3.4028235E38
double          64              1.0E102         4.9E-324             1.7976931348623157E308
==============  =============   ============    =============        =============

[edit] Printing Booleans and Characters

The boolean primitive has only two values true and false. Let's use these values to produce a truth table for the Exclusive-OR (XOR) operator which is implemented in Java as the ^ operator.

<<XOR table>>=
public static void truthTable() {
  String table =
          "  A   \t   B   \t A XOR B\n" +
          "======\t=======\t========\n" +
          false + "\t" + false + "\t" + (false ^ false) + "\n" +
          false + "\t" + true  + "\t" + (false ^ true ) + "\n" +
          true  + "\t" + false + "\t" + (true  ^ false) + "\n" +
          true  + "\t" + true  + "\t" + (true  ^ true ) + "\n" +
         "======\t=======\t========\n";
  System.out.println(table);
}
<<Printing objects>>=
truthTable();

The result is:

  A   	   B   	 A XOR B
======	=======	========
false	false	false
false	true	true
true	false	true
true	true	false
======	=======	========

To illustrate the printing of character values we'll take a string "Now is the time for all good men to come to the aid of the party", convert it into an array of char, perform a 5 character shift cypher on each character and then print the resulting cypher text.

<<simple cypher>>=
public static String simpleCypher(String plaintext) {
   char [] letters = plaintext.toCharArray();
   String cypherText = "";
   for (int i = 0; i < plaintext.length(); i ++) {
     char letter = plaintext.charAt(i);
	 letter += 5;
	 cypherText += letter;
   }
   return cypherText;
}
<<Printing objects>>=
System.out.println(simpleCypher("Now is the time for all good men to come to the aid of the party"));

Here is the result:

 St|%nx%ymj%ynrj%ktw%fqq%ltti%rjs%yt%htrj%yt%ymj%fni%tk%ymj%ufwy~


[edit] Printing Strings

Complete me

A discussion of what happens when String.toString is called.

[edit] Printing Metaclass information

Complete Me

What is printed when Class.toString() is called and how Object.toString() is able to call upon this meta class behaviour to print something for any object.

[edit] A Selection of further Examples from the API

Complete Me

A selection of examples to illustrate what is printed by other classes for example Data, List, Map, RegExp. This will help us to formulate a discussion of how to write our own toString methods.

[edit] Program 2

This program exercises all of the examples from the discussion of "More about Object.toString{)."

Complete Me

<<PrintingObjects.java>>=
include a selection of classes from the API

public class PrintingObjects {
	public static void main (String[] args) {
	  Printing objects
	}

	print table of numbers
	XOR table
	simple cypher
}

[edit] Converting Java Primitives to Strings

As discussed in the first part of this article, when called upon to print a primitive value, the Java compiler will insert code to wrap the primitive in an instance of the primitive type's wrapper class. It will then call the wrapper class's toString() method. Thus, when compiled:

long l = 1000000L;
String ls = "" + l;

will actually produce code equivalent to:

long l = 1000000L;
String ls = "" + new Long(l).toString();

In this section, we explore this behaviour in more detail. Complete Me

[edit] Define the test fixture

To demonstrate the behaviour of automatic string coercion, we'll first create a set of primitives:

<<Declare the primitives>>=
boolean flag = false;
char c = 'A';
byte b = (byte)127;
short s = (short)32767;
int i  = 10000000;
long l = 1000000000000L;
float f = 3.1415926F;
double d = 1.0e99;

Note you need to use a cast with the byte and short literals because the Java compiler will always convert a literal integer into an int. Similarly the postfixes L and F are needed because otherwise Java will compile an integer literal as an int and a floating point literal as a double.

To make the code of the test itself easier to understand, we create wrapper classes for each of these cases and add them to an array list.

<<Wrap the primitives and add to an array list>>=
ArrayList primitives = new ArrayList();
Boolean wflag = new Boolean(flag); primitives.add(wflag);
Character wc = new Character(c); primitives.add(wc);
Byte wb = new Byte(b); primitives.add(wb);
Short ws = new Short(s); primitives.add(ws);
Integer wi = new Integer(i); primitives.add(wi);
Long wl = new Long(l); primitives.add(wl);
Float wf = new Float(f); primitives.add(wf);
Double wd = new Double(d); primitives.add(wd);

[edit] Defining the test

For testing purposes, we want to take each element of the array list, convert the object to a string using Object.toString(), and compare the resulting string with the result of automatic String coercion that results from "" + value:

<<Test primitive to string conversion>>=
for (int primitive = 0; primitive < primitives.size(); primitive++) {
  Object wrapper = primitives.get(primitive);
  System.out.println("Testing toString for " + wrapper.getClass());
  get result of toString
  get result of automatic string coercion
  compare results
}

All wrapper classes override the toString(), so to get a printable string we simply call this method. There is no need to force a cast, as the correct version of toString method will be called at run time due to polymorphism.

<<get result of toString>>=
String stringFromWrapper = wrapper.toString();

The proof that wrapper.toString() is the same as the "" + value will be trickier because Java's static type checking will not allow us to easily extract the value stored in the wrapper class. For now let us postulate that we can create a static method coerceWrappedPrimitiveValueToString(Object) that will take a wrapper object and return the value of the wrapped primitive coerced to a string. With this method available we can write:

<<get result of automatic string coercion>>=
String coercedFromValueOfPrimitive = coerceWrappedPrimitiveValueToString(wrapper);

and the final test is now simply:

<<compare results>>=
assert stringFromWrapper.equals(coercedFromValueOfPrimitive);

[edit] Validating the auto string coercion

We want to prove that the general statement "" + primitive is converted by the compiler to new Wrapper(primitive).toString().

<<define a method to coerce a primitive to a string>>=
static String coerceWrappedPrimitiveValueToString(Object o) {
   process according to type of wrapper object
}

Unfortunately, Java's static type checking makes the coercion of the primitive value of a wrapper class much more difficult than the simple polymorphic call to wrapper.toString(). There is no equivalent polymorphic call valueOf that can return a primitive of undefined type. Instead, each wrapper class provides method with signature type typeValue(). We will need to use run-time type identification (RTTI) to cast the object parameter to its wrapper class before we can extract the value and coerce the value to a string.

We will need to identify the class of the wrapper object o before we can assign the wrapped primitive value to a value of the correct type. We will use a decision tree for this:

<<process according to type of wrapper object>>=
if (o instanceof Boolean) {
  return value of Boolean coerced to String
}
else if (o instanceof Character) {
  return value of Character coerced to String
}
else if (o instanceof Byte) {
  return value of Byte coerced to String
}
else if (o instanceof Short) {
  return value of Short coerced to String
}
else if (o instanceof Integer) {
  return value of Integer coerced to String
}
else if (o instanceof Long) {
  return value of Long coerced to String
}
else if (o instanceof Float) {
  return value of Float coerced to String
}
else if (o instanceof Double) {
  return value of Double coerced to String
}
else {
  unexpected class: return default coercion
}

Having decided which wrapper class we are dealing with, we can get at the primitive type and coerce it to a string. Here's the example for the Long case. You'll note that we have to downcast the reference from Object to Long before we can access the longValue() method:

<<return value of Long coerced to String>>=
long l = ((Long)o).longValue();
return "" + l;

The other types follow the same pattern:

<<return value of Boolean coerced to String>>=
boolean b = ((Boolean)o).booleanValue();
return "" + b;
<<return value of Character coerced to String>>=
char c = ((Character)o).charValue();
return "" + c;
<<return value of Byte coerced to String>>=
byte aByte = ((Byte)o).byteValue();
return "" + aByte;
<<return value of Short coerced to String>>=
short s = ((Short)o).shortValue();
return "" + s;
<<return value of Integer coerced to String>>=
int i = ((Integer)o).intValue();
return "" + i;
<<return value of Float coerced to String>>=
float f = ((Float)o).floatValue();
return "" + f;
<<return value of Double coerced to String>>=
double d = ((Double)o).doubleValue();
return "" + d;

Finally, for the case where no match is found, we simply let Object.toString() do its thing:

<<unexpected class: return default coercion>>=
return "" + o;

[edit] Program 3

The complete test class is:

<<ConvertPrimitivesToStrings.java>>=
import java.util.ArrayList;

public class ConvertPrimitivesToStrings {

  public static void main (String [] args) {
     System.out.println("Demonstrating that Java's built-in support for converting primitives\n" +
        "to Strings relies on the toString method");
     Declare the primitives

     Wrap the primitives and add to an array list

     Test primitive to string conversion
  }

  define a method to coerce a primitive to a string

}

[edit] Using Automatic String Generation in your own Classes

[edit] The Default behaviour

Complete Me

Will discuss what happens when you don't override Object.toString when you write your own classes...

[edit] Writing Your Own toString Method

... and the benefits you and your clients will get for free when you do.

[edit] Using Jakarta Commons

For more complex classes, it can become tedious to have to write consistently good consistent toString methods. The Apache Jakarta Commons library comes to the rescue here with the org.apache.commons.lang.builder.ToStringBuilder(). We will illustrate the use of this in a custom class of own own and demonstrate some of its other features, such as the use of reflection to completely automate the creation of a toString method, or even to interrogate foreign objects. This particular section may be a little too advanced for this introductory article and it may benefit from presentation in a separate article.

Complete Me

[edit] Discussion

Note: needs revision! Clearly you can call toString() yourself whenever you need a printable version of an object or a primitive, but arguably the compiler's default behaviour allows you to write clearer and cleaner code.

[edit] References

Joshua Block, Effective Java, Sun Microsystems Press, Addison Wesley, 2002.

Bruce Eckel, Thinking in Java, 4th Edition, Prentice-Hall, 2006.

[edit] See Also

Download code
hijacker
hijacker
hijacker
hijacker