2.5. Wrapper classes

[2.5] Develop code that uses wrapper classes such as Boolean, Double, and Integer.

Java defines a wrapper class for each of its primitive data types. The wrapper classes are used to wrap primitives in an object, so they can be added to a collection object. They enable all types to be treated like object instances. Wrapper classes help you write cleaner code, which is easy to read. For this exam, you should be able to write code that uses these wrapper classes.

2.5.1. Class hierarchy of wrapper classes

All the wrapper classes are immutable—classes that don’t allow changes to the state of their instances after initialization. They share multiple usage details and methods. Figure 2.18 shows their hierarchy.

Figure 2.18. Hierarchy of wrapper classes

All the numeric wrapper classes extend the class java.lang.Number. Classes Boolean and Character directly extend the class Object. All the wrapper classes implement the interfaces java.io.Serializable and java.lang.Comparable. All these classes can be serialized to a stream, and their objects define a natural sort order.

2.5.2. Creating objects of the wrapper classes

You can create objects of all the wrapper classes in multiple ways:

  • Assignment— By assigning a primitive to a wrapper class variable (autoboxing)
  • Constructor— By using wrapper class constructors
  • Static methods— By calling static method of wrapper classes, like, valueOf()

For example:

You can create objects of the rest of the wrapper classes (Short, Integer, Long, and Float) in a similar manner. All the wrapper classes define constructors to create an object using a corresponding primitive value or as a String.

Another interesting point to note is that neither of these classes defines a default no-argument constructor. The wrapper classes are immutable. So it doesn’t make sense to initialize the wrapper objects with the default primitive values if they can’t be modified later.

Exam Tip

All wrapper classes (except Character) define a constructor that accepts a String argument representing the primitive value that needs to be wrapped. Watch out for exam questions that include a call to a no-argument constructor of a wrapper class. None of these classes define a no-argument constructor.

You can assign a primitive value directly to a reference variable of its wrapper class type—thanks to autoboxing. The reverse is unboxing, when an object of a primitive wrapper class is converted to its corresponding primitive value. I’ll discuss autoboxing and auto-unboxing, in detail, in the next section.

2.5.3. Retrieving primitive values from the wrapper classes

All wrapper classes define methods of the format primitiveValue(), where the term primitive refers to the exact primitive data type name. Table 2.13 shows a list of the classes and their methods to retrieve corresponding primitive values.

Table 2.13. Methods to retrieve primitive values from wrapper classes

Boolean

Character

Byte, Short, Integer, Long, Float, Double

booleanValue() charValue() byteValue(), shortValue(), intValue(),
    longValue(), floatValue(), doubleValue()

It’s interesting to note that all numeric wrapper classes define methods to retrieve the value of the primitive value they store, as a byte, short, int, long, float, or double.

Exam Tip

All six numeric wrapper classes inherit all six ***Value() methods from their common superclass, Number.

2.5.4. Parsing a string value to a primitive type

To get a primitive data type value corresponding to a string value, you can use the static utility method parseDataType, where DataType refers to the type of the return value. Each wrapper class (except Character) defines a method to parse a String to the corresponding primitive value, as listed in table 2.14.

Table 2.14. List of parseDataType methods in wrapper classes

Class name

Method

Boolean public static boolean parseBoolean(String s)
Character no corresponding parsing method
Byte public static byte parseByte(String s)
Short public static short parseShort (String s)
Integer public static int parseInt(String s)
Long public static long parseLong(String s)
Float public static float parseFloat(String s)
Double public static double parseDouble(String s)

All these parsing methods throw NumberFormatExceptions for invalid values. Here are some examples:

Exam Tip

All parse methods (listed in table 2.14) throw NumberFormat-Exception except Boolean.parseBoolean(). This method returns false whenever the string it parses is not equal to “true” (case-insensitive comparison).

2.5.5. Difference between using the valueOf method and constructors of wrapper classes

The valueOf() method returns an object of the corresponding wrapper class when it’s passed an argument of a primitive type or String. So what is the difference between the valueOf() method and constructors of these classes, which also accept method arguments of a primitive type and String?

Wrapper classes Byte, Short, Integer, and Long cache objects with values in the range of -128 to 127. The Character class caches objects with values 0 to 127. These classes define inner static classes that store objects for the primitive values -128 to 127 or 0 to 127 in an array. If you request an object of any of these classes, from this range, the valueOf() method returns a reference to a predefined object; otherwise, it creates a new object and returns its reference:

Exam Tip

Wrapper classes Float and Double don’t cache objects for any range of values.

In the case of the Boolean class, the cached instances are accessible directly because only two exist: static constants Boolean.TRUE and Boolean.FALSE.

2.5.6. Comparing objects of wrapper classes

You can compare objects of wrapper classes for equality by using the method equals or the comparison operator, that is, ==. Method equals() always compares the primitive value stored by a wrapper instance, and == compares object references. The operator == returns true if the variables being compared to refer to the same instance.

Refer to the preceding section on valueOf(). Wrapper classes like Character, Byte, Short, Integer, and Long cache wrapper objects for values 0 to 127 or -128 to 127. Depending on how you initialize wrapper instances, they might or might not refer to the same instances. The following example initializes Integer variables using constructors, the static method valueOf, and autoboxing (covered in the next section). Let’s compare these references using ==:

System.out.println(i1 == i2);
System.out.println(i3 == i4);
System.out.println(i4 == i5);
System.out.println(i5 == i6);

Here’s the output of the preceding code:

false
true
true
true

As evident from the output of the preceding code, Integer instances created using the method valueOf and autoboxing for int value 10 refer to the same instance. If you replace == with equals() in the preceding lines of code, they will output true:

But the same isn’t applicable for Integer instances created for int value 200 and compared using == (because they aren’t stored in the Integer cache):

Again, if you replace == with equals() in the preceding code, the code will output true for all comparisons.

Exam Tip

Cached instances exist for the wrapper Boolean class for the values true and false. The Character class caches instances with values from 0 to 127. Classes Byte, Short, Integer, and Long cache instances for values -127 to 128. No cached instances exist for the Float and Double wrapper classes.

The method equals compares the values stored by wrapper instances. The comparison operator == compares reference variables—checking whether they refer to the same instance.

Using hashCode() and equals() to determine equality of wrapper class instances

Instances of wrapper classes can be used with the Java collection framework, as keys, with classes that support key-value pairs (like HashMap). These classes use hashCode() and equals() to determine the equality of instances. Because the collection framework classes (apart from ArrayList) aren’t on this exam, I don’t cover them in this book.

You can’t compare wrapper instances for equality using equals() or ==, if they aren’t of the same class. The code won’t compile for instances that are compared using ==. When compared using equals(), the output will be false:

Exam Tip

Objects of different wrapper classes with same values are not equal. Using equals() with such instances will return false. If you use == with such instances, the code won’t compile.

The next section covers autoboxing and unboxing, used by the compiler to convert primitive values to wrapper objects and vice versa.

2.5.7. Autoboxing and unboxing

Autoboxing is the automatic conversion of a primitive data type to an object of the corresponding wrapper class (you box the primitive value). Unboxing is the reverse process (you unbox the primitive value), as shown in figure 2.19.

Figure 2.19. Autoboxing and unboxing

The wrapper classes use autoboxing and unboxing features quite frequently:

Compare the use of the preceding method against the following method defined by the class Double:

public int compareTo(Double anotherDouble)

Wait—did I just mention that the compareTo() method defined in the class Double accepts an object of the class Double and not a double primitive data type? Then why does the preceding code compile? The answer is autoboxing. Java converted the primitive double to an object of the class Double (by using the valueOf() method), so it works correctly. The Java compiler converted it to the following at runtime:

Double d1 = new Double(12.67D);
System.out.println(d1.compareTo(Double.valueOf(21.68D)));

Now examine the following code (an example of unboxing with autoboxing):

In the preceding code, at the end of execution of the for loop, total will be assigned a Double value of 23.36. The arithmetic operators like += can’t be used with objects. So why do you think the code compiles? In this example, the Java compiler converted the preceding code to the following at runtime:

public class Unbox {
    public static void main(String args[]) {
        ArrayList list = new ArrayList();
        list.add(new Double(12.12D));
        list.add(new Double(11.24D));
        Double total = Double.valueOf(0.0D);
        for(Iterator iterator = list.iterator(); iterator.hasNext();) {
            Double d = (Double)iterator.next();
            total = total.doubleValue() + d.doubleValue();
        }
    }

In the previous section, I mentioned that wrapper classes are immutable. So what happens when you add a value to the variable total, a Double object? In this case, the variable total refers to a new Double object.

Exam Tip

Wrapper classes are immutable. Adding a primitive value to a wrapper class variable doesn’t modify the value of the object it refers to. The wrapper class variable is assigned a new object.

Here’s another interesting question. What happens if you pass null as an argument to the following method?

public int increment(Integer obj) {
    return ++obj;
}

Because the Java compiler would call obj.intValue() to get obj’s int value, passing null to the increment() method will throw a NullPointerException.

Exam Tip

Unboxing a wrapper reference variable, which refers to null, will throw a NullPointerException.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.202.61