| Java 2 Basics > Converting and Casting | Index - Previous - Next |
Conversion of primitive types may occur in these contexts:
General rules for primitive assignment conversions:
int i = 5; float j = i;
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.
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.
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
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 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.
byte x = 1; x++; // Ok. x is now equal to 2. x = x + 1; // Error. Expression x + 1 is promoted to int.
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.
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.
byte a = 15; a = -a; // Error. -a is promoted to int. a = ~a; // Error. ~a is promoted to int.
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.
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.
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.
int a = -128; byte x = (byte)a; float a = -128.0f; byte x = (byte)a; Result in both cases: -128
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.
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.
int a = 129; byte x = (byte)a; Result: -127
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.
int a = 257; byte x = (byte)a; Result: 1 257 = [00000000] [00000000] [00000001] [00000001] 32-bits int value 1 = [00000001] 8-bits byte value
int a = -135; byte x = (byte)a; Result: 121 -135 = [11111111] [11111111] [11111111] [01111001] 32-bits int value 121 = [01111001] 8-bits byte value
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.
int a = 65535; char x = (char)a; Result: 65535
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.
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
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
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.
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.
double a = 1.9999999; int x = (int)a; Result: 1
Object reference conversion takes place in:
(There is no arithmetic promotion)
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:
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.
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.
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.
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.
The rules for method-call conversion are the same as the rules for assignment conversion.
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
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).
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.
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 |