Object Wrappers and Autoboxing

   


Occasionally, you need to convert a primitive type like int to an object. All primitive types have class counterparts. For example, a class Integer corresponds to the primitive type int. These kinds of classes are usually called wrappers. The wrapper classes have obvious names: Integer, Long, Float, Double, Short, Byte, Character, Void, and Boolean. (The first six inherit from the common superclass Number.) The wrapper classes are immutable you cannot change a wrapped value after the wrapper has been constructed. They are also final, so you cannot subclass them.

Suppose we want an array list of integers. Unfortunately, the type parameter inside the angle brackets cannot be a primitive type. It is not possible to form an ArrayList<int>. Here, the Integer wrapper class comes in. It is ok to declare an array list of Integer objects.

 ArrayList<Integer> list = new ArrayList<Integer>(); 

CAUTION

An ArrayList<Integer> is far less efficient than an int[] array because each value is separately wrapped inside an object. You would only want to use this construct for small collections when programmer convenience is more important than efficiency.


Another JDK 5.0 innovation makes it easy to add and get array elements: The call

 list.add(3); 

is automatically translated to

 list.add(new Integer(3)); 

This conversion is called autoboxing.

NOTE

You might think that autowrapping would be more consistent, but the "boxing" metaphor was taken from C#.


Conversely, when you assign an Integer object to an int value, it is automatically unboxed. That is, the compiler translates

 int n = list.get(i); 

into

 int n = list.get(i).intValue(); 

Automatic boxing and unboxing even works with arithmetic expressions. For example, you can apply the increment operator to a wrapper reference:

 Integer n = 3; n++; 

The compiler automatically inserts instructions to unbox the object, increment the resulting value, and box it back.

In most cases, you get the illusion that the primitive types and their wrappers are one and the same. There is just one point in which they differ considerably: identity. As you know, the == operator, applied to wrapper objects, only tests whether the objects have identical memory locations. The following comparison would therefore probably fail:

 Integer a = 1000; Integer b = 1000; if (a == b) ... 

However, a Java implementation may, if it chooses, wrap commonly occurring values into identical objects, and thus the comparison might succeed. This ambiguity is not what you want. The remedy is to call the equals method when comparing wrapper objects.

NOTE

The autoboxing specification requires that boolean, byte, char 127, and short and int between -128 and 127 are wrapped into fixed objects. For example, if a and b had been initialized with 100 in the preceding example, then the comparison would have had to succeed.


Finally, let us emphasize that boxing and unboxing is a courtesy of the compiler, not the virtual machine. The compiler inserts the necessary calls when it generates the bytecodes of a class. The virtual machine simply executes those bytecodes.

The wrapper classes exist since JDK 1.0, but before JDK 5.0, you had to insert the boxing and unboxing code by hand.

You will often see the number wrappers for another reason. The designers of Java found the wrappers a convenient place to put certain basic methods, like the ones for converting strings of digits to numbers.

To convert a string to an integer, you use the following statement:

 int x = Integer.parseInt(s); 

This has nothing to do with Integer objects parseInt is a static method. But the Integer class was a good place to put it.

The API notes show some of the more important methods of the Integer class. The other number classes implement corresponding methods.

CAUTION

Some people think that the wrapper classes can be used to implement methods that can modify numeric parameters. However, that is not correct. Recall from Chapter 4 that it is impossible to write a Java method that increments an integer parameter because parameters to Java methods are always passed by value.

 public static void triple(int x) // won't work {    x = 3 * x; // modifies local variable } 

Could we overcome this by using an Integer instead of an int?

 public static void triple(Integer x) // won't work {    ... } 

The problem is that Integer objects are immutable: the information contained inside the wrapper can't change. You cannot use these wrapper classes to create a method that modifies numeric parameters.


NOTE

If you do want to write a method to change numeric parameters, you can use one of the holder types defined in the org.omg.CORBA package. There are types IntHolder, BooleanHolder, and so on. Each holder type has a public (!) field value tHRough which you can access the stored value.

 public static void triple(IntHolder x) {    x.value = 3 * x.value; } 



 java.lang.Integer 1.0 

  • int intValue()

    returns the value of this Integer object as an int (overrides the intValue method in the Number class).

  • static String toString(int i)

    returns a new String object representing the number i in base 10.

  • static String toString(int i, int radix)

    lets you return a representation of the number i in the base specified by the radix parameter.

  • static int parseInt(String s)

    returns the integer value of the string s, assuming it represents an integer in base 10.

  • static int parseInt(String s, int radix)

    returns the integer value of the string s, assuming it represents an integer in the base specified by the radix parameter.

  • static Integer valueOf(String s)

    returns a new Integer object initialized to the integer's value, assuming the specified String represents an integer in base 10.

  • static Integer valueOf(String s, int radix)

    returns a new Integer object initialized to the integer's value, assuming the specified String represents an integer in the base specified by the radix parameter.


 java.text.NumberFormat 1.1 

  • Number parse(String s)

    returns the numeric value, assuming the specified String represents a number.

Methods with a Variable Number of Parameters

Before JDK 5.0, every Java method had a fixed number of parameters. However, it is now possible to provide methods that can be called with a variable number of parameters. (These are sometimes called "varargs" methods.)

You have already seen such a method: printf. For example, the calls

 System.out.printf("%d", n); 

and

 System.out.printf("%d %s", n, "widgets"); 

both call the same method, even though one call has two parameters and the other has three.

The printf method is defined like this:

 public class PrintStream {    public PrintStream printf(String fmt, Object... args) { return format(fmt, args); } } 

Here, the ellipsis ... is a part of the Java code. It denotes that the method can receive an arbitrary number of objects (in addition to the fmt parameter).

The printf method actually receives two parameters, the format string, and an Object[] array that holds all other parameters. (If the caller supplies integers or other primitive type values, autoboxing turns them into objects.) It now has the unenviable task of scanning the fmt string and matching up the ith format specifier with the value args[i].

In other words, for the implementor of printf, the Object... parameter type is exactly the same as Object[].

The compiler needs to transform each call to printf, bundling the parameters into an array and autoboxing as necessary:

 System.out.printf("%d %d", new Object[] { new Integer(d), "widgets" } ); 

You can define your own methods with variable parameters, and you can specify any type for the parameters, even a primitive type. Here is a simple example: a function that computes the maximum of a variable number of values.

 public static double max(double... values) {    double largest = Double.MIN_VALUE;    for (double v : values) if (v > largest) largest = v;    return largest; } 

Simply call the function like this:

 double m = max(3.1, 40.4, -5); 

The compiler passes a new double[] { 3.1, 40.4, -5 } to the max function.

NOTE

It is legal to pass an array as the last parameter of a method with variable parameters, for example

 System.out.printf("%d %s", new Object[] { new Integer(1), "widgets" } ); 

Therefore, you can redefine an existing function whose last parameter is an array to a method with variable parameters, without breaking any existing code. For example, MessageFormat.format was enhanced in this way in JDK 5.0.



       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net