GARBA.ORG
Java 2 Basics > Converting and Casting Index - Previous - Next

Converting and Casting

Primitives

Conversion of primitive types may occur in these contexts:

Widening conversions

Assignment

General rules for primitive assignment conversions:

Example #1:

	int i = 5;
	float j = i;

Method call

Widening conversion takes place on method calls as on assignments. You can pass to a method any primitive narrower than the expected one. Implicit casting will naturally occur.

For instance:

	public static void main(String args[]) {
		byte x = 126;
		System.out.println( DoIt(x) );
	}

	static String DoIt(int a) {
		return "I've received an int of value "+a;
	}

	Result: I've received an int value of 126

The method DoIt(int a) excpects an int, but you can throw a char, byte or short there, the value will be promoted to int and the method will IN FACT receive an int.

This special behavior ocurs if a method to handle a narrower type hasn't been declared. If you declare a method to handle bytes, then that method will "catch" the call. This is an OOP feature called overloading.

Example:

	public static void main(String args[]) {
		byte x = 126;
		System.out.println( DoIt(x) );
	}

	static String DoIt(int a) {
		return "I've received an int of value "+a;
	}
	static String DoIt(byte a) {
		return "I've received a byte of value "+a;
	}

	Result: I've received a byte value of 126

If the argument type if wider than expected, no implicit casting will occur and you will need to perform an explicit cast:

	public static void main(String args[]) {
		float x = 1.26f;
		System.out.println( DoIt( (int)x ) );
	}

	static String DoIt(int a) {
		return "I've received an int of value "+a;
	}

	Result: I've received an int value of 1

Last example:

	public static void main(String args[]) {
		char x = 'A';
		System.out.println( DoIt(x) );
	}

	static String DoIt(int a) {
		return "I've received an int of value "+a;
	}
	static String DoIt(byte a) {
		return "I've received a byte of value "+a;
	}

	Result: I've received an int value of 65

As you can see, there's no method to catch char types so the value is promoted to int and caught by DoIt(int a).

Arithmetic Promotion

Arithmetic promotion happens when narrower types need to be promoted to wider types in order to make sense in an operation among other wider types.

Basic rules for binary operators and most of the unary operators:

These rules don't apply to the unary operators: ++ and -- and assignment operators.

Example #1:

	byte x = 1;
	x++;          // Ok. x is now equal to 2.
	x = x + 1;    // Error. Expression x + 1 is promoted to int.

Example #2:

	byte a = 1;
	byte x = 23;
	x <<= a;      // Ok. x is now equal to 46.
	x = x << a;   // Error. Expression x << a is promoted to int.

Example #3:

	char a = 5;
	short x = 3;
	x *= a;       // Ok. x is now equal to 15.
	x = x * a;    // Error. Expression x = x * a is promoted to int.

Example #4:

	byte a = 15;
	a = -a;       // Error. -a is promoted to int.
	a = ~a;       // Error. ~a is promoted to int.

Example #5:

	float a = 1.0f;
	int b = 15;
	int x = a * b;       // Error. Expression is promoted to float.
	int x = (int)(a*b);  // Ok. We cast the float result back to int.

Primitives and Casting

Casting means explicitly telling Java to make a conversion. A casting operation may widen or narrow its argument. To cast a value, you need to precede it with the name of the desired type enclosed within parentheses:

	byte x = 15;
	int r = (int)(x * 3);

Booleans cannot be casted. Don't bother with them, stuff like this doesn't work:

	byte a = 1;
	boolean status = a;

Narrowing runs the risk of loosing information, in fact, many times you know that you are going to loose information and it is important to know which part information is going to be loosen and naturally, what information will be kept.

Casting to (byte)

Within the range:

A byte is signed and can hold a range of numbers from -128 to 127. If the value of the widest type is within this range, conversion won't produce unexpected results.

Example:

	int a = -128;
	byte x = (byte)a;

	float a = -128.0f;
	byte x = (byte)a;

	Result in both cases: -128

Outside the range but within the signed byte range:

If the value is between 128 and 255, it will be converted to binary and then to the byte decimal representation of that binary pattern. In fact, this bit-level interpretation always occurs, but you have to be conscious about it for this special case.

Example:

	int a = 128;
	byte x = (byte)a;

	Result: -128

The bit pattern for 128 is 10000000, but 10000000 is considered to be a signed byte. Thus 10000000 is equal to -128. The next binary number, 10000001, equals to -127. If byte was unsigned, as in C/C++, the decimal value of 1000001 would be 129.

Example:

	int a = 129;
	byte x = (byte)a;

	Result: -127

Outside the signed byte range:

If the value is greater than 255 or lower than -128, the lower byte of the value is kept and the rest is just thrown away.

Example #1:

	int a = 257;
	byte x = (byte)a;

	Result: 1

	257 = [00000000] [00000000] [00000001] [00000001]
	                 32-bits int value      

	                                   1 = [00000001]
	                                  8-bits byte value 

Example #2:

	int a = -135;
	byte x = (byte)a;

	Result: 121
		
	-135 = [11111111] [11111111] [11111111] [01111001]
	                 32-bits int value      

	                                  121 = [01111001]
	                                  8-bits byte value

Casting to (char)

Within the range:

A char is 16-bits wide unsigned type that holds values between 0 and 65535. Conversion will perform as expected if the value is within the valid range.

Example:

	int a = 65535;
	char x = (char)a;

	Result: 65535

Outside the range or negative:

If the value is outside the range because it is lower than 0 or greater than 65535, then the lower 2 bytes will be kept.

Example #1:

	int a = 65539;
	char x = (char)a;

	Result: 3

      65539 = [00000000] [00000001] [00000000] [00000011]
	                 32-bits int value      

	                          3 = [00000000] [00000011]
	                               16-bits char value

Example #2:

	int a = -1;
	char x = (char)a;

	Result: 65535

         -1 = [11111111] [11111111] [11111111] [11111111]
	                   32-bits int value      

	                      65535 = [11111111] [11111111]
	                               16-bits char value

Casting to (short) and other signed integer values

Values between -32768 and 32767 are converted flawlessly as they are within the valid range. If the value is lower or greater, the lower 2 bytes of the value will be kept to conform a short value. The behaviour is the same as byte casting.

Other integer values will also behave as expected according to what we've seen at the byte examples.

Casting floats or doubles to narrower types

On some programming languages, conversion from floating-point numbers to decimals "round" the value. This is not the Java case, the integer part is kept and the rest is thrown away.

Example:

	double a = 1.9999999;
	int x = (int)a;

	Result: 1

Object Reference Conversion

Object reference conversion takes place in:

(There is no arithmetic promotion)

Assignment

Object reference assignment conversion happens when you assign an object reference value to a variable of a different type. There are 3 general kinds of object reference type:

Conversion rules for implicit casting on this context:

		OLD_Type a = new OLD_Type;
		NEW_Type b = a;		
OLD_Type = Class OLD_Type = Interface OLD_Type = Array
NEW_Type = Class OLD_Type must be a subclass of NEW_Type. NEW_Type must be Object. NEW_Type must be Object.
NEW_Type = Interface OLD-Type must implement interface NEW_Type OLD_Type must be a subinterface of NEW_Type NEW_Type must be Cloneable or Serializable
NEW_Type = Array Compiler ERROR Compiler ERROR OLD_Type must be an array of some object reference type that can be converted to what- ever NEW-Type is an array of

Conversions are usually permited when NEW_Type is a "up" in the inheritance hierarchy.

Example #1, Subclass / Class implicit casting:

	class Animal {}
	class Dog extends Animal {}

	Dog a = new Dog();         
	Animal b = a;              // Ok. Animal is the superclass of Dog.

	Animal x = new Animal();
	Dog y = x;                 // Error. Dog is a subclass of Animal.

	Animal l = new Animal();   // Ok. Object is above Animal in the Class
	Object m = l;	         // hierarchy.

A class may be converted to a class type or to an interface type. If converting to a class type, the new type must be a superclass of the old type.

Example #2, Class / Interface implicit casting:

	interface Eatable {}

	class Animal {}
	class Dog extends Animal implements Eatable {}

	Eatable e;
	Animal a;
	Object o;

	e = new Dog();      // Ok. Dog implements Eatable 
	e = new Animal();   // Error. Animal doesn't implements Eatable

	a = e;     // Error. An interface may not be converted to a Class 
	o = e;     // Ok. An interface may be converted to an Object.

A class may be converted to a class type or an interface type. If converting to a interface type, the old class must implement the interface.

An interface type may only be converted to an interface type or to Object. If the new type is an interface, it must be a superinterface of the old type.

Example #3, Arrays:

	class Animal {}
	class Dog extends Animal {}

	Animal canines[] = new Animal[25];
	for (int i = 0 ; i < canines.length ; i++) {
		canines[i] = new Animal();
	}

	Object some_canines[] = new Object[25];
	Animal dangerous_canines[] = new Animal[25];
	Dog friendly_canines[] = new Dog[25];
		
	some_canines = canines;
	dangerous_canines = canines;
	some_canines[5] = canines [5];
	friendly_canines = canines;       // Error. 
	friendly_canines[5] = canines[5]; // Error. Incompatible types	

An array me be converted to the class Object, to the interface Clonable or Serializable, or to an array. Only an array of object references may be converted to an array, and the old element type must be convertible to the new element type.

Method-call

The rules for method-call conversion are the same as the rules for assignment conversion.

Object Reference Casting

Compile-time rules for object reference casting:

OLD_Type = Non-final class OLD_Type = A final class OLD_Type = Interface OLD_Type = Array
NEW_Type = Non-Final Class OLD_Type must extend NEW_Type or vice versa OLD_Type must extend NEW_Type Always OK NEW_Type must be Object
NEW_Type = Final Class NEW_Type must extend OLD_Type OLD_Type and NEW_Type must be the same class NEW_Type must implement Cloneable or Serializable Compiler ERROR
NEW_Type = Interface Always OK OLD_Type must implement interface NEW_Type Always OK Compiler ERROR
NEW_Type = Array OLD_Type must be Object not NEW_Type Compiler ERROR Compiler ERROR OLD_Type must be array of some type that can be cast to whatever NEW_Type is an array

Example #1, Class / Subclass casting:

	class Animal {}
	class Dog extends Animal {}
	class Cat extends Animal {}

	Animal a = new Animal();
	Dog d = new Dog();
	Cat c = new Cat();

	d = (Dog)a;    // Ok. Compiles but produces a runtime Exception.
	d = (Dog)c;    // Error. Won't compile.
	a = (Animal)c; // Ok. Compiles and RUNS.

When both OLD_Type and NEW_Type are classes, one class must be a subclass of the other (it doesn't matter which one).

Example #2, Interface / Class casting:

	class Animal implements Eatable {}
	class Dog extends Animal {}
	interface Eatable {}

	Eatable e;
	Animal a; 
	Dog d = new Dog();

	e = d;       // Ok. Eatable is implemented by Animal and hence by Dog. 
	d = e;       // Error. An interface cannot be converted to a class.
	d = (Dog)e;  // Ok. Compiles/RUNS The class held by e is convertible.

Implicitly converting an interface to a class is never allowed. You need to use an explicit cast to force the compiler to do something where unexpected results may arise.

Example #3, Arrays:

	class Animal {}
	class Dog extends Animal {}

	Dog terriers[] = new Dog[25];
	Animal canines[] = new Animal[25];

	canines = terriers;        // Ok. Animal is more general than Dog
	terriers = (Dog[])canines; // Ok. Tell the compiler it's Ok.

An array cast is legal if casting the array element types is legal (and if the element types are references, not primitives). If we remove the line

	canines = terriers;

the program will compile but an Exception will be raised at runtime because when we say that terriers = (Dog[])canines; we tell the compiler: "don't worry, they will be compatible types". They certainty won't, as terriers belong to the Dog class and canines to the Animal class. The Dog class is a subclass of Animal.


© Ernesto Garbarino Top