Java Type Casting Issues
Category Programming Technology
Java type casting issues are not complicated, as long as you remember one phrase: a superclass reference points to a subclass object.
What does it mean for a superclass reference to point to a subclass object? Let me explain it slowly.
Let's start with two terms: upcasting and downcasting.
For example, there are two classes, Father
is the superclass, and Son
inherits from Father
.
Example 1:
Father f1 = new Son(); // This is called upcasting
// Now f1 references a Son object
Son s1 = (Son)f1; // This is called downcasting
// Now f1 still references a Son object
Example 2:
Father f2 = new Father();
Son s2 = (Son)f2; // Error, a subclass reference cannot point to a superclass object
You might ask, in the first example: Son s1 = (Son)f1; Why is it correct?
It's simple because f1 points to a subclass object, Father f1 = new Son();
The subclass s1 reference can certainly point to a subclass object.
But f2 is assigned to a Father object, Father f2 = new Father();
The subclass s2 reference cannot point to a superclass object.
Summary:
A superclass reference points to a subclass object, but a subclass reference cannot point to a superclass object.
Assigning a subclass object directly to a superclass reference is called upcasting, and no explicit type casting is needed, such as:
Father f1 = new Son();
Assigning a superclass reference that points to a subclass object to a subclass reference is called downcasting, and explicit type casting is required, such as:
f1 is a superclass reference that points to a subclass object. Assigning f1 to the subclass reference s1, i.e., Son s1 = (Son)f1;
The (Son) before f1 must be added for explicit type casting.
I. Upcasting.
Colloquially, this means converting a subclass object to a superclass object. The superclass object here can be an interface.
1. Method invocation in upcasting:
Example
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
class Bird extends Animal{
public void eat(){
System.out.println("bird eatting...");
}
public void fly(){
System.out.println("bird flying...");
}
}
class Main{
public static void main(String[] args) {
Animal b=new Bird(); // Upcasting
b.eat();
//! error: b.fly(); b points to a subclass object, but the fly() method is lost
dosleep(new Male());
dosleep(new Female());
}
public static void dosleep(Human h) {
h.sleep();
}
}
Example
public class Human {
public void sleep() {
System.out.println("Human sleep..");
}
}
class Male extends Human {
@Override
public void sleep() {
System.out.println("Male sleep..");
}
}
class Female extends Human {
@Override
public void sleep() {
System.out.println("Female sleep..");
}
}
Note the upcasting here:
Animal b=new Bird(); // Upcasting
b.eat();
The subclass's eat() method will be called. Reason: b actually points to a Bird subclass object, so the subclass's method will be called.
It's important to note that during upcasting, b will lose other methods that are not shared with the superclass object. For example, the fly method is no longer accessible to b.
2. Benefits of upcasting
Look at the code above:
public static void dosleep(Human h) {
h.sleep();
}
Using the superclass as a parameter, sometimes with a subclass as a parameter, takes advantage of upcasting. This makes the code more concise. Otherwise, if dosleep used subclass objects as parameters, a function would need to be written for each subclass. This also reflects Java's abstract programming philosophy.
II. Downcasting.
Opposite to upcasting, this means converting a superclass object to a subclass object.
Example
package com.wensefu.other1;
public class Girl {
public void smile(){
System.out.println("girl smile()...");
}
}
class MMGirl extends Girl{
@Override
public void smile() {
System.out.println("MMirl smile sounds sweet...");
}
public void c(){
System.out.println("MMirl c()...");
}
}
class Main{
public static void main(String[] args) {
Girl g1=new MMGirl(); // Upcasting
g1.smile();
MMGirl mmg=(MMGirl)g1; // Downcasting, both compile and run without errors
mmg.smile();
mmg.c();
Girl g2=new Girl();
// MMGirl mmg1=(MMGirl)g2; // Unsafe downcasting, compile error-free but runtime error
// mmg1.smile();
// mmg1.c();
/*output:
* CGirl smile sounds sweet...
* CGirl smile sounds sweet...
* CGirl c()...
* Exception in thread "main" java.lang.ClassCastException: com.wensefu.other1.Girl
* at com.wensefu.other1.Main.main(Girl.java:36)
*/
if(g2 instanceof MMGirl){
MMGirl mmg1=(MMGirl)g2;
mmg1.smile();
mmg1.c();
}
}
}
Girl g1=new MMGirl(); // Upcasting
g1.smile();
MMGirl mmg=(MMGirl)g1; // Downcasting, both compile and run without errors
This downcasting is safe. Because g1 points to a subclass object.
But
Girl g2=new Girl();
MMGirl mmg1=(MMGirl)g2; // Unsafe downcasting, compile error-free but runtime error
Runtime error:
Exception in thread "main" java.lang.ClassCastException: com.wensefu.other1.Girl
at com.wensefu.other1.Main.main(Girl.java:36)
As shown in the code, you can use instanceof to prevent exceptions.