Difference between revisions of "Autoboxing"

From Suhrid.net Wiki
Jump to navigationJump to search
 
(11 intermediate revisions by the same user not shown)
Line 64: Line 64:
 
}
 
}
 
}
 
}
 +
 +
</syntaxhighlight>
 +
 +
== Assignments ==
 +
 +
* Note since Wrappers are not compatible types, assignments to wrappers is tricky.
 +
* For Wrappers smallers than "Int", the compiler automatically puts in a cast (like it does for primitives)
 +
 +
<syntaxhighlight lang="java5">
 +
 +
Short s = 5; //OK compiler adds an implicit cast for 5 to short which is then wrapped to Short
 +
 +
Int i = 10; //OK - Standard boxing
 +
 +
Long l = 20; //NOT OK - Compiler does not add a cast, an int is NOT-A Long so this fails.
 +
 +
Long l = 20l; //Make it explicitly long - Now OK
 +
 +
Float f = 1.3; //NOT OK - 1.3 is double by default
 +
 +
Float f = 1.3f; //Now OK
 +
 +
Double d = 4.5; //OK - 4.5 is double by default.
 +
 +
short s2 = 11;
 +
 +
Integer iref = s2; //NOT OK. a Short is NOT-A Integer
 +
 +
</syntaxhighlight>
 +
 +
 +
* Similar to primitives, any two operations involving int literals is an int
 +
 +
<syntaxhighlight lang="java5">
 +
 +
Byte b1 = 10;
 +
Byte b2 = 20;
 +
 +
Byte c3 = b1 + b2 ; //NOT OK
 +
 +
Byte c3 = (byte) b1 + b2; //OK - note cast is for byte and not Byte.
 +
 +
 +
</syntaxhighlight>
 +
 +
* Compound assignment operators are not allowed on Wrappers smaller than Integer
 +
* Because the result of the operation in an integer which cannot be cast to the Wrapper Type.
 +
 +
<syntaxhighlight lang="java5">
 +
 +
Short s1 = 10;
 +
 +
s1 *= 10; //Wont work
 +
 +
s1 = (short) (s1 * 10); //This is OK
 +
 +
</syntaxhighlight>
 +
 +
* Assigning Wrappers to primitives
 +
 +
* For the same type unboxing will happen.
 +
* For narrower types casting needs to be added.
 +
 +
<syntaxhighlight lang="java5">
 +
 +
Integer iref = 10;
 +
 +
short s = (short) iref.intValue();
 +
 +
Byte bo = 20;
 +
 +
long lp = bo; //OK - bo is unboxed to a byte which can be widened to a long
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 205: Line 277:
  
 
* '''These wont work:'''
 
* '''These wont work:'''
 +
 +
<syntaxhighlight lang="java5">
 +
 +
void go() {
 +
  int(3, 4); //AMBIGUOUS
 +
 +
  int[] ia = {3,4};
 +
  foo(ia); //OK
 +
  Integer[] IA = {3,4};
 +
  foo(IA); //OK
 +
}
 +
 +
void foo(int... ia) {
 +
 +
}
 +
 +
 +
void foo(Integer... ia) {
 +
 +
}
 +
 +
 +
</syntaxhighlight>
  
 
<syntaxhighlight lang="java5">
 
<syntaxhighlight lang="java5">
Line 269: Line 364:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
== Overriding ==
 +
 +
* Autoboxing/Unboxing doesnt work when it comes to overriding methods return types. This is true while extending classes also.
 +
 +
<syntaxhighlight lang="java5">
 +
 +
 +
interface A {
 +
 +
public int foo();
 +
 +
public Integer bar();
 +
}
 +
 +
class B implements A {
 +
 +
public Integer foo() { //WONT COMPILE - has to return int
 +
return new Integer(42);
 +
}
 +
 +
public int bar() { //WONT COMPILE - has to return Integer
 +
return 42;
 +
}
 +
}
 +
 +
 +
</syntaxhighlight>
 +
 +
* However, things are OK when it comes to method parameters when extending classes.
 +
 +
<syntaxhighlight lang="java5">
 +
 +
class A {
 +
 +
public void foo(int i) {
 +
 +
}
 +
 +
public void bar(Integer iref) {
 +
 +
}
 +
}
 +
 +
class B extends A {
 +
 +
public void foo(Integer iref) {  //OK
 +
 +
}
 +
 +
public void bar(int i) { //OK
 +
 +
}
 +
}
 +
 +
</syntaxhighlight>
 +
 +
* But '''NOT''' for interfaces !
 +
 +
<syntaxhighlight lang="java5">
 +
 +
interface  A {
 +
 +
public void foo(int i) ;
 +
 +
public void bar(Integer iref);
 +
}
 +
 +
class B implements A {
 +
 +
public void foo(Integer iref) {
 +
 +
}
 +
 +
public void bar(int i) {
 +
 +
}
 +
}
 +
 +
</syntaxhighlight>
  
 
[[Category:OCPJP]]
 
[[Category:OCPJP]]

Latest revision as of 09:35, 13 September 2011

Wrapper Classes

  • Wrapper classes for primtives are a mechanism to include primitives in activities reserved for objects. e.g. being part of Collections.
  • Wrapper objects are immutable !
  • All have two constructors - one takes a primitive, other a string representation.
  • Static convenience methods:
    • valueOf() method also takes a string and returns a wrapper object in return. Also accepts an optional base (for Octal, Hex etc)
    • String to primitive - e.g. Integer.parseInt("22"), Double.parseDouble("3.14"); Note: the parseXXX methods throw a runtime NumberFormatException if the string cannot be parsed.
    • Base conversion is possible through static utility methods in Integer and Long - e.g. toBinaryString(), toHexString() and toOctalString().
  • Instance method:
    • Wrapper to primitive - use the xxxValue() methods like intValue() and floatValue()
    • toString() returns the string representation of the value represented by the wrapper.
  • Boolean wrapper takes a String argument (through constructor or valueOf method). If String is equivalent to "true/TRUE/True" etc- case INsensitive, then the Boolean value represents true, else it represents false (for all other string values INCLUDING NULL)

Autoboxing Intro

  • New feature in Java 5 which avoids having to manually wrap and unwrap a primitive.
  • Almost everywhere, the wrapper and the primitive are interchangeable and the wrapper can be used like a primitive.
  • But since wrappers are immutable, any "change" in the wrappers value leads to a new wrapper object being created. See below: incrementing y means y will refer to a new object.
Integer y = new Integer(42);
Integer x = y;
System.out.println(x==y); //Prints true
y++;
System.out.println(x==y); //Prints false

Equality

  • Primitives and Wrappers can be compared for equality using the '==' operator. The wrapper will be automatically unboxed.
  • Wrapper's equals() method will check if the values are the same.
  • Like the string pool, JVM tries to save memory when wrappers are used, so a pool is maintained for certain values of wrappers.
  • The '==' operator will return true when for wrapper references when their values are the same and they:
    • All Boolean Wrappers
    • All Byte Wrappers
    • Character from \u0000 to \u007f
    • Short and Integer from -128 to 127
  • Note, these are for literal values only. Any Wrapper created using the new operator will always be a separate object.
public class Equality {

	public static void main(String[] args) {
		
		Integer i1 = 64;
		Integer i2 = 64;
		
		System.out.println(i1 == i2); //True
		
		Integer i3 = new Integer(111);
		Integer i4 = new Integer(111);
		
		System.out.println(i3 == i4); //False
		
		Character c1 = '\u0000';
		Character c2 = '\u0000';
		
		System.out.println(c1 == c2); //True
		
	}
}

Assignments

  • Note since Wrappers are not compatible types, assignments to wrappers is tricky.
  • For Wrappers smallers than "Int", the compiler automatically puts in a cast (like it does for primitives)
Short s = 5; //OK compiler adds an implicit cast for 5 to short which is then wrapped to Short

Int i = 10; //OK - Standard boxing

Long l = 20; //NOT OK - Compiler does not add a cast, an int is NOT-A Long so this fails.

Long l = 20l; //Make it explicitly long - Now OK

Float f = 1.3; //NOT OK - 1.3 is double by default

Float f = 1.3f; //Now OK

Double d = 4.5; //OK - 4.5 is double by default.

short s2 = 11;

Integer iref = s2; //NOT OK. a Short is NOT-A Integer


  • Similar to primitives, any two operations involving int literals is an int
Byte b1 = 10;
Byte b2 = 20;

Byte c3 = b1 + b2 ; //NOT OK

Byte c3 = (byte) b1 + b2; //OK - note cast is for byte and not Byte.
  • Compound assignment operators are not allowed on Wrappers smaller than Integer
  • Because the result of the operation in an integer which cannot be cast to the Wrapper Type.
Short s1 = 10;

s1 *= 10; //Wont work

s1 = (short) (s1 * 10); //This is OK
  • Assigning Wrappers to primitives
  • For the same type unboxing will happen.
  • For narrower types casting needs to be added.
Integer iref = 10;
		
short s = (short) iref.intValue();

Byte bo = 20;

long lp = bo; //OK - bo is unboxed to a byte which can be widened to a long

Overloading

  • In every case when an exact match isnt found - the JVM uses the method with the smallest argument that is wider than the parameter.


  • Widening is preferred over Boxing. This is to allow legacy code to function the way it used to.
public class OverloadTest {

	public static void main(String[] args) {
		int i = 42;
		foo(i); 
	}
	
	static void foo(Integer x) {
		System.out.println("Box");
	}
	
	static void foo(float f) {
		System.out.println("Widen"); //Widen will print.
	}

}
  • Widening is prefered over Var-Args. Again, this is to preserve legacy code behavior because var-args is a Java 5 feature.
        static void foo() {
             byte a = 10;
             byte b = 20;
             go(a, b);
        }


        static void go(byte... x) {
		System.out.println("Var Args");
	}
	
	static void go(long x, long y) {
		System.out.println("Widen"); //Widen will print.
	}
  • Finally, Boxing beats Var-args. This is because var-args is more broader than boxing, since the varargs can accept any number of arguments.
public static void main(String[] args) {
		int a = 10;
		int b = 20;
		foo(a, b);
}
	
static void foo(Integer x, Integer y) {
		System.out.println("Boxing"); //Will print Boxing
}
	
static void foo(long ... la) {
		System.out.println("Var-Args");
}


  • Illegal to widen wrapper classes ! Because Integer is NOT A Long and a Short is NOT A Integer.
  • An int can be boxed to an Integer, it can fit into a long, but cannot be matched to a Long.
public static void main(String[] args) {
		int i = 10;
		bar(i); //Compiler Error
	}
	
	static void bar(Long x) {
		System.out.println("Widen");
	}
  • However this will work because an int can be boxed to an Integer which IS-A Object.
public static void main(String[] args) {
		byte b = 10;
		bar(i); //Compiler Error
	}
	
	static void bar(Object x) {
		System.out.println("Box-and-Widen"); //Will print "Box-and-Widen".
	}
  • Note it is OK to assign int literals within permissible range to Wrapper types. The compiler adds in a cast automatically (like how it does for primitive types)
  • However, for a widening assignment for e.g. an int assigned to a Long, since an int is NOT a Long, the compiler does not add a cast automatically and this will result in a compile ERROR.
Byte b = 100; //OK
Short s = 500; //OK

Long l = 10; // ERROR. 

Long l = 10L; //FIXED 
//OR

Long l = (long) 10; //Add a cast


  • Widening and Boxing can be combined with var-args. This is NOT overloading though.
static void go() {
   int i = 10, j = 20;
   foo(i, j) //OK
   bar(i, j) //OK
}

static void foo(long... la) {
}

static void bar(Integer... ia) {
}
  • These wont work:
void go() {
  int(3, 4); //AMBIGUOUS
 
  int[] ia = {3,4};
  foo(ia); //OK
  Integer[] IA = {3,4};
  foo(IA); //OK
}

void foo(int... ia) {

}


void foo(Integer... ia) {

}
void go() {
   foo(3, 4); //Compiler Error - Amibiguous.
} 

void foo(int... la) {
}

void foo(long... la) {
}


void go() {
   byte a = 10;
   byte b = 20;
   foo(a, b); //Compiler Error - Amibiguous.
} 

void foo(int... la) {
}

void foo(Byte... la) {
}


  • SUMMARY:

(From http://www.coderanch.com/t/417622/java-programmer-SCJP/certification/Golden-Rules-widening-boxing-varargs )

  • Widening > Boxing > Varargs.
  • Widening and Boxing (WB) not allowed. (int is NOT-A Long)
  • Boxing and Widening (BW) allowed. (int is an Integer IS-A Object)
  • Only while overloading, Widening + vararg and Boxing + vararg cannot be used together. Can be used separately, that is if methods have different names as in example above.
  • Widening between wrapper classes not allowed
Overloaded methods               Invoked by saying     Called method
doX(Integer i) & doX(long l)       doX(5)              long (by Rule 1)
doX(int...i) & doX(Integer i)      doX(5)              Integer (by Rule 1)
doX(Long l) & doX(int...i)         doX(5)              int...i (Rule 2 & 1)
doX(Long l) & doX(Integer...i)     doX(5)              Integer...i(R. 2&1)
doX(Object o) & doX(Long l)        doX(5)              Object o (Rule 2&3)
doX(Object o) & doX(int...i)       doX(5)              Object o (Rule 3&1)
doX(Object o) & doX(long l)        doX(5)              long l (Rule 3&1)
doX(long...l) & doX(Integer...i)   doX(5)              ambiguous (Rule 4)
doX(long...l) & doX(Integer i)     doX(5)              Integer (Rule 1)
doX(Long l)                        Integer i;          error (Rule 5)
                                   doX(i)
doX(Long l) & doX(long...l)        Integer i;          long...l(Rule 5 & 1)
                                   doX(i)

Overriding

  • Autoboxing/Unboxing doesnt work when it comes to overriding methods return types. This is true while extending classes also.
interface A {
	
	public int foo();
	
	public Integer bar();
}

class B implements A {
	
	public Integer foo() { //WONT COMPILE - has to return int
		return new Integer(42);
	}
	
	public int bar() { //WONT COMPILE - has to return Integer
		return 42;
	}
}
  • However, things are OK when it comes to method parameters when extending classes.
class A {
	
	public void foo(int i) {
		
	}
	
	public void bar(Integer iref) {
		
	}
}

class B extends A {
	
	public void foo(Integer iref) {  //OK
		
	}
	
	public void bar(int i) { //OK
		
	}
}
  • But NOT for interfaces !
interface  A {
	
	public void foo(int i) ;
	
	public void bar(Integer iref);
}

class B implements A {
	
	public void foo(Integer iref) {
		
	}
	
	public void bar(int i) {
		
	}
}