Java Wrapper classes are the way to treat primitive data types of Java as an object. This is required especially when we need an object reference to a primitive value because the type of functionality required by their natural atomic form, such as int, char, double, Boolean, and so forth, would not suffice. This article delves into the idea of these classes provided in the standard API library.
Primitive and Reference Types in Java
In Java, types are designated as either primitive or reference types.
Primitive types those that store single declared types of a value at a time. For example, a variable defined as int can store a whole number at a time. At an instance when another value is stored, the initial value is replaced by the new one. In Java, primitive variables are initialized by default. If it is a numeric type, such as designated by byte, short, int, long, float, and double, it is initialized to 0 (zero), the char type is initialized to the numeric value of -1 and the Boolean type is initialized to false by default. The initial default value may be overwritten by assigning a value in its declaration.
However, note that locally declared primitive values are not initialized by default. Any attempt to access the local uninitialized variable is a compile-time error.
All other variables that are not of primitive types are actually reference types. Reference types hold references to memory objects. Each of the referenced objects may contain many instance variables. They are initialized by default to a null value, meaning, references nothing or no memory object. Reference type variables have a class definition with declared methods and properties. They are instantiated with the help of constructors invoked via the new keyword; typically, the object instance created is used to access the methods defined within them.
Person person=new Person();
Java Wrapper Classes
Java wrapper classes are designed to store primitive types as reference types and treat them as a reference to a memory object upon instantiation. The primary benefit to this is that the corresponding wrapper class of a particular primitive type can hold a lot of information of that type apart for providing functions such a conversion from one type to another and a whole lot of other convenient functionalities. Because there are eight primitive types in Java, the corresponding wrapper classes are as follows:
|Wrapper Class||Primitive Types||Description|
|Boolean||Boolean||This wrapper class is wrapped around a Boolean type where the Boolean values are true and false.|
|Character||char||This wrapper class is wrapped around a primitive single field char type. The data in the Character are defined by the information in the UnicodeData. Reference: www.unicode.org.|
|Byte||byte||This wrapper class is wrapped around a primitive byte type.|
|Double||double||This wrapper class is wrapped around a primitive double type.|
|Float||float||This wrapper class is wrapped around a primitive float type.|
|Integer||int||This wrapper class is wrapped around a primitive int type.|
|Long||long||This wrapper class is wrapped around a primitive long type.|
|Short||short||This wrapper class is wrapped around a primitive short type.|
|Void||void||Wrapper class for void. Unlike other wrapper classes, this class cannot be instantiated. It simply signifies the idea of a void reference.|
These wrapper classes are defined in the java.lang package and are hierarchically structured as follows:
Figure 1: The wrapper classes' hierarchical structure
To put it simply, wrapper classes are the way to create an object reference type of primitive types in Java. Jestingly, we may say that with the provision of wrapper classes, Java has armed itself to the teeth in the essence of object-oriented technology where even primitive types are also not spared.
Jests apart, a question that comes to the mind that, by having two types of data to manage (primitive values and object reference), isn't Java posing a problem under certain circumstances? The answer is no, because here we have both options open. On occasions where we need an object that serves as a container to hold a simple primitive type which provides a rich set of functionalities in addition to simply holding the value, there is no other way than by using the wrapper classes. For example, an object created from the Integer class stores a single int value also provides numerous static and non-static functions. A few of them are as follows:
A constructor that constructs an Integer object with the supplied value of:
There is another constructor that creates an Integer object with the int value, indicated by the String parameter. If the String parameters do not contain a parsable integer, it throws a NumberFormatException.
Some of the member functions are:
byte byteValue() double doubleValue() float floatValue() int intValue() long longValue() shortValue()
These member functions return the appropriate primitive type values of the values represented by Integer object references.
Also, there are many static methods, such as:
static int max(int a, int b) static int min(int a, int b)
Which returns greater and smaller of the two int values, respectively.
Refer to the Java API documentation for a complete list of functions provided by the Integer wrapper class and other wrapper classes.
Auto Boxing and Auto-unboxing
This feature slightly sets apart the wrapper classes in view of other classes. Prior to Java 5, the conversion of simple primitive types and fit into the data structure of the wrapper class were a bit inconvenient. We had to create an object instance of the wrapper every time and then were able only to insert the primitive value into the object reference. Similar inconveniences were there for retrieving the value as well. For example, we had to do something like this:
Integer iArray = new Integer iArray = new Integer(100);
And, to retrieve the value, we had to do as follows:
int value = iArray.intvalue();
With the introduction of auto boxing and auto-unboxing post Java 5, the above condition can be achieved with less cumbersome code, such as what follows:
Integer iArray = new Integer; iArray = 100; int value = iArray;
The auto boxing in this regard means the conversion of a value of primitive type to a corresponding type-wrapper object reference. And, auto-unboxing means the conversion of an object of the type-wrapper object reference to a value of the corresponding primitive type. Because this is done automatically without the intervention of the developer, the operation is called auto boxing and auto-unboxing, respectively.
In the preceding code, auto boxing occurs in the statement
iArray = 100;
The int value 100 is automatically wrapped up as an Integer object reference and stored. The value thus stored is no longer a primitive type but now is an object reference.
Similarly, auto-unboxing occurs in the statement
int value = iArray;
Here, the object reference of the wrapper class Integer is converted into a primitive value and stored in the primitive type int variable value.
Wrapper classes are convenience classes and they should be treated as such. If we are to compare the performance benefits obtained between using wrapper classes and primitive types, it is obvious that the primitive types always must be used. Also, another important point to be keep in mind is that if we are to pass a primitive value by reference to a function, we must use an object reference (wrapper class) because Java passes primitive types by value to methods.