Autoboxing/unboxing of primitive types


 

Autoboxing/unboxing of primitive types

In this SCJP section, you will learn about Autoboxing/unboxing of primitive types.

In this SCJP section, you will learn about Autoboxing/unboxing of primitive types.

Autoboxing/unboxing of primitive types

When adding a primitive data type,conversion between primitive types and wrapper classes is necessary  to a collection. For example, an int is stored and then fetched from an ArrayList:

list.add(0, new Integer(59));
int n = ((Integer)(list.get(0))).intValue();
					

This new feature eliminates this manual conversion. The above lines can be written as:

list.add(0, 59);
int total = list.get(0);
					

However, the wrapper class, Integer for example, must be used as a generic type:

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

					

The autoboxing and auto-unboxing of Java primitives produces code that is more concise and easier to follow.

int i = 10;

Integer iRef = new Integer(i);  // Explicit Boxing
int j = iRef.intValue();        // Explicit Unboxing

iRef = i;	// Automatic Boxing
j = iRef;	// Automatic Unboxing					
					

 In Boxing  ,it converts primitive values to wrapper types:  if p is a value of a primtiveType, boxing converts p into corresponding WrapperType, such that ref.primitiveTypeValue() == p.

In Unboxing , it converts objects of wrapper types to primitive types: if ref is a reference of a WrapperType, unboxing  converts the reference ref into ref.primitiveTypeValue().

Assignment conversions on boolean and numeric types:

boolean boolVal = true;
byte b = 2;
short s = 2;
char c ='2';
int i = 2;

// Boxing
Boolean boolRef = boolVal;
Byte bRef = 88; 			
Short sRef = 2; 
Character cRef = '2';
Integer iRef = 2;
// Integer iRef1 = s; // WRONG ! short not assignable to Integer

// Unboxing
boolean boolVal1 = boolRef;
byte b1 = bRef;
short s1 = sRef;
char c1 = cRef;
int i1 = iRef;
					

Method invocation conversions on actual parameters:

...					
flipFlop("(String, Integer, int)", new Integer(4), 2004);
...
private static void flipFlop(String str, int i, Integer iRef) {
	out.println(str + " ==> (String, int, Integer)");
}
					
The output:
(String, Integer, int) ==> (String, int, Integer)					
					

Casting conversions:

Integer iRef = (Integer) 2;     // Boxing followed by identity cast
int i = (int) iRef;             // Unboxing followed by identity cast
// Long lRef = 2;               // WRONG ! Type mismatch: cannot convert from int to Long
// Long lRef = (Long) 2;        // WRONG ! int not convertible to Long
Long lRef_1 = (long) 2;         // OK ! explicit cast to long
Long lRef_2 = 2L;               // OK ! long literal
					

Numeric promotion: unary and binary:

Integer iRef = 2;
long l1 = 2000L + iRef;     // binary numeric promotion
int i = -iRef;              // unary numeric promotion
					

In the if statement, condition can be Boolean:

					
Boolean expr = true;
if (expr) {
	out.println(expr);
} else {
	out.println(!expr); // Logical complement operator
}	
					

In the switch statement, the switch expression can be Character, Byte, Short or Integer:

// Constants
final short ONE = 1;
final short ZERO = 0;
final short NEG_ONE = -1;

// int expr = 1; // (1) short is assignable to int. switch works.
// Integer expr = 1; // (2) short is not assignable to Integer. switch compile error.
Short expr = 1; // (3) OK. Cast is not required.
switch (expr) { // (4) expr unboxed before case comparison.
	case ONE:
		out.println("ONE"); break;
	case ZERO:
		out.println("ZERO"); break;
	case NEG_ONE:
		out.println("NEG_ONE"); break;
	default:
		assert false;
}					
					
The output:
ONE					
					

In the while, do-while and for statements, the condition can be Boolean:

Boolean expr = true;
while (expr) {
	expr = !expr;
}	
					
Character[] version = { '5', '.', '0' };    // Assignment: boxing
for (Integer iRef = 0;                      // Assignment: boxing
        iRef < version.length;           // Comparison: unboxing
        ++iRef) {                           // ++: unboxing and boxing
    out.println(iRef + ": " + version[iRef]); // Array index: unboxing
}	
					
Output:
0: 5
1: .
2: 0
					

Boxing and unboxing in collections/maps:

					
String[] words = new String[] {"aaa", "bbb", "ccc", "aaa"};
Map<String, Integer> m = new TreeMap<String, Integer>();
for (String word : words) {
	Integer freq = m.get(word);
	m.put(word, freq == null ? 1 : freq + 1);
}
out.println(m);
			
					
The output:
{aaa=2, bbb=1, ccc=1}
					

In the next example an int is being stored and then retrieved from an ArrayList. The J2SE 5.0 leaves the conversion required to transition to an Integer and back to the compiler.

Before:

					
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = (list.get(0)).intValue();
					
					

After:

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, 42);
int total = list.get(0);
					
					

Here is another sample program featuring autoboxing and unboxing. It is a static factory that takes an int array and returns a List of Integer backed by the array. This method provides the full richness of the List interface atop an int array. All changes to the list write through to the array and vice-versa:

// List adapter for primitive int array
public static List<Integer> asList(final int[] a) {
	return new AbstractList<Integer>() {
		
		public Integer get(int i) { return a[i]; }

		// Throws NullPointerException if val == null		
		public Integer set(int i, Integer val) {
			Integer oldVal = a[i];
			a[i] = val;
			return oldVal;
		}
        
		public int size() { return a.length; }
	};
}
					
					

Wrappers and primitives comparison:

public class WrappersTest {
	public static void main(String[] s) {
		Integer i1 = new Integer(2);
		Integer i2 = new Integer(2);
		System.out.println(i1 == i2); // FALSE

		Integer j1 = 2;
		Integer j2 = 2;
		System.out.println(j1 == j2); // TRUE

		Integer k1 = 150;
		Integer k2 = 150;
		System.out.println(k1 == k2); // FALSE

		Integer jj1 = 127;
		Integer jj2 = 127;
		System.out.println(jj1 == jj2); // TRUE

		int jjj1 = 127;
		Integer jjj2 = 127;
		System.out.println(jjj1 == jjj2); // TRUE

		Integer kk1 = 128;
		Integer kk2 = 128;
		System.out.println(kk1 == kk2); // FALSE

		Integer kkk1 = 128;
		int kkk2 = 128;
		System.out.println(kkk1 == kkk2); // TRUE

		Integer w1 = -128;
		Integer w2 = -128;
		System.out.println(w1 == w2); // TRUE

		Integer m1 = -129;
		Integer m2 = -129;
		System.out.println(m1 == m2); // FALSE
		
		int mm1 = -129;
		Integer mm2 = -129;
		System.out.println(mm1 == mm2); // TRUE		
	}
}					
					

NOTE, certain primitives are always to be boxed into the same immutable wrapper objects. These objects are then CACHED and REUSED, with the expectation that these are commonly used objects. These special values are:

  • The boolean values true and false.

  • The byte values.

  • The short and int values between -128 and 127.

  • The char values in the range '\u0000' to '\u007F'.

    Character c1 = '\u0000';
    Character c2 = '\u0000';
    System.out.println("c1 == c2 : " + (c1 == c2)); // TRUE !!!
    		
    Character c11 = '\u00FF';
    Character c12 = '\u00FF';
    System.out.println("c11 == c12 : " + (c11 == c12)); // FALSE
    								
    c1 == c2 : true
    c11 == c12 : false
    								

The following example gives NullPointerException:

Integer i = null;
int j = i; // java.lang.NullPointerException !!!
					

This example demonstrates methods resolution when selecting overloaded method:

public static void main(String[] args) {
	doSomething(1);
	doSomething(2.0);
}

public static void doSomething(double num) {
	System.out.println("double : " + num);
}

public static void  doSomething(Integer num) {
	System.out.println("Integer : " + num);
}
					
The output is:
double : 1.0
double : 2.0
					
Java 5.0 will always select the same method that would have been selected in Java 1.4.

The method resolution inludes three passes:

0
  1. Attempt to locate the correct method WITHOUT any boxing, unboxing, or vararg invocations.

  2. Attempt to locate the correct method WITH boxing, unboxing, and WITHOUT any vararg invocations.

  3. Attempt to locate the correct method WITH boxing, unboxing, or vararg invocations.

    1

String

The String class represents character strings. All string literals in Java programs, such as "abc", are implemented as instances of this class.

All implemented interfaces: Serializable, CharSequence, Comparable<String>.

2

This method returns the number of characters in the string as in:

int length ()
					

This example results in variable x holding the value 8:

3
String str = "A string";
int x = str.length ();
					

Removes whitespace from the leading and trailing edges of the string:

String trim ()
					

This results in the variable str referencing the string "14 units":

String string = "  14 units  ";
String str = string.trim ();
					

The following methods return the index, starting from 0, for the location of the given character in the string. (The char value will be widened to int):

4
int indexOf (int ch)
int lastIndexOf (int ch)
					

For example:

String string = "One fine day";
int x = string.indexOf ('f');					
					
This results in a value of 4 in the variable x. If the string holds NO such character, the method returns -1.

To continue searching for more instances of the character, you can use the method:

5
indexOf(int ch, int fromIndex)
					

This will start the search at the fromIndex location in the string and search to the end of the string.

The methods:

6
indexOf (String str)
indexOf (String str, int fromIndex)
					

provide similar functions but search for a sub-string rather than just for a single character.

Similarly, the methods:

7
lastIndexOf (int ch) 
lastIndexOf (int ch, int fromIndex)

lastIndexOf (String str) 
lastIndexOf (String str, int fromIndex)
					

search backwards for characters and strings starting from the right side and moving from right to left. (The fromIndex second parameter still counts from the left, with the search continuing from that index position toward the beginning of the string).

These two methods test whether a string begins or ends with a particular substring:

boolean startsWith (String prefix)
boolean endsWith (String str)					
					
For example:
String [] str = {"Abe", "Arthur", "Bob"};
for (int i=0; i < str.length (); i++) {
	if (str1.startsWith ("Ar")) doSomething ();
}
					

The first method return a new string with all the characters set to lower case while the second returns the characters set to upper case:

8
String toLowerCase ()
String toUpperCase ()
					
Example:
String [] str = {"Abe", "Arthur", "Bob"};
for (int i=0; i < str.length(); i++){
    if (str1.toLowerCase ().startsWith ("ar")) doSomething ();
}

Ads