Method Dispatch and Casting
Applied class exercises for week 4, covering java-polymorphism (dynamic method dispatch) and java-casting in more depth.
On the HTML site, fill in each blank with your answer (as a quoted string, e.g. "Car.topSpeed()"), then click Run Code to check it. In the PDF, the Working callout is shown as a static answer key instead (interactive checking needs a browser).
Question 1 — Apparent type vs actual type
Every object has an apparent type (compile-time type, from variable/method declarations) and an actual type (runtime type, fixed by whichever class’s new constructed it). The compiler always ensures the apparent type is the actual type or one of its superclasses, decides whether a call is legal using the apparent type, but dispatches the call to the method as implemented by the actual type.
class Animal { void eat() {...} }
class Lion extends Animal {}
class Dog extends Animal implements Pet {
void eat() {...}
void eat(int howMuch) {...}
}
class Poodle extends Dog {}
interface Pet {}What happens when you call simba.eat(200), given Lion simba = new Lion();?
Lion’s apparent AND actual type is Lion, which has no eat(int) method (only the no-arg eat() inherited from Animal) — the compiler rejects the call outright, since legality is checked against the apparent type’s available methods.
Full worked example, one line at a time:
| Statement | Outcome |
|---|---|
Dog evie = new Dog(); |
OK |
Animal animal = evie; |
OK (upcast, implicit) |
Lion mufasa = animal; |
Must be cast. Will fail (an Animal referencing a Dog is never a Lion). |
Dog harlee = animal; |
Must be cast. Will succeed (the actual object really is a Dog). |
Poodle bruiser = evie; |
Must be cast. Will fail (evie’s actual type is Dog, not Poodle). |
Lion simba = new Lion(); |
OK |
Lion scar = new Poodle(); |
Will not compile — Poodle and Lion share no inheritance relationship. |
Pet charley = new Poodle(); |
OK (Poodle extends Dog, which implements Pet). |
Dog lassie = charley; |
Must be cast. Will succeed. |
Animal scooby = charley; |
Will not compile — Pet is not related to Animal (an interface doesn’t know about unrelated classes). |
simba.eat(); |
OK |
simba.eat(200); |
No method found — compile error. |
Question 2 — A larger hierarchy
class Location {...}
interface RoadLegal { int topSpeed(); boolean licenceRequired(); }
interface PublicTransport { double calculateFare(Location start, Location end); }
class Vehicle {
int travelTime(Location start, Location end) {...}
int topSpeed() {...}
}
class HumanPowered extends Vehicle {
int getWeight() {...}
int topSpeed() {...}
}
class Motorised extends Vehicle implements RoadLegal {
int travelTime(Location start, Location end) {...}
int topSpeed() {...}
boolean licenceRequired() {...}
}
class Skateboard extends HumanPowered {}
class Bicycle extends HumanPowered implements RoadLegal {
boolean licenceRequired() {...}
}
class Motorbike extends Motorised {
int topSpeed() {...}
int travelTime(Location location) {...} // overloaded, single-Location version
}
class Car extends Motorised { int topSpeed() {...} }
class Taxi extends Car {}
class Bus extends Motorised implements PublicTransport {
int topSpeed() {...}
double calculateFare(Location start, Location end) {...}
}classDiagram
Vehicle <|-- HumanPowered
Vehicle <|-- Motorised
HumanPowered <|-- Skateboard
HumanPowered <|-- Bicycle
RoadLegal <|.. Bicycle
Motorised <|-- Motorbike
Motorised <|-- Car
Motorised <|.. RoadLegal
Car <|-- Taxi
Motorised <|-- Bus
Bus <|.. PublicTransport
For each, what method implementation is actually called?
Vehicle vehicle = new Taxi(); vehicle.topSpeed(); →
Vehicle vehicle = new Bicycle(); vehicle.topSpeed(); →
RoadLegal roadLegal = new Bicycle(); roadLegal.topSpeed(); →
Vehicle vehicle = new Motorbike(); vehicle.travelTime(location1, location2); →
Even though roadLegal’s apparent type is the RoadLegal interface, dispatch always goes to the actual type’s implementation — Bicycle doesn’t override topSpeed() itself, so it’s inherited from HumanPowered. Similarly Motorbike doesn’t override the two-Location travelTime, only the single-Location overload, so the two-argument call dispatches to Motorised’s version.
Some other statements to check yourself against — whether each compiles, and why:
Vehicle bus = new Bus();— compiles (upcast).Motorised skateboard = new Skateboard();— does not compile: no relationship betweenMotorisedandSkateboard.Motorbike vehicle = new Vehicle();— does not compile: downcast needs an explicit cast.Motorised motorbike = new Motorbike(); motorbike.travelTime(destination);— does not compile: neitherMotorisednor its parents declare a single-argumenttravelTime(Location).Car car = new Taxi(); Taxi taxi = car;— does not compile: downcast needs an explicit cast.Vehicle bike = new Bicycle(); bike.getWeight();— does not compile:Vehiclehas nogetWeight()method (it’s declared onHumanPowered).Motorised bus = new Bus(); PublicTransport trip = bus;— does not compile:Motorisedcan’t be implicitly cast to the unrelatedPublicTransportinterface.
Question 3 — Casting rules
When changing a value’s apparent type:
- Casting to a superclass of the current apparent type can be implicit.
- Casting to a subclass should be checked via
instanceof(a runtime check). - If the two types share no inheritance relationship, Java refuses to compile at all.
For each instanceof pattern-match below (assuming each variable holds an instance of its own class, e.g. bus is a Bus), does it compile, and if so, could the match be guaranteed true at compile time ("implicit")?
| Expression | Compiles? | Guaranteed true? |
|---|---|---|
bus instanceof Vehicle vehicle |
Yes | Yes — Vehicle is a superclass of Bus |
car instanceof Taxi taxi |
Yes | No — Taxi is a subclass of Car, needs a runtime check |
motorised instanceof Bicycle bike |
No | — Motorised and Bicycle share no relationship (siblings under Vehicle) |
motorbike instanceof RoadLegal legal |
Yes | Yes — Motorbike extends Motorised, which implements RoadLegal |
taxi instanceof Vehicle vehicle |
Yes | Yes — Vehicle is a superclass all the way up the Taxi → Car → Motorised → Vehicle chain |
Where would an electric scooter fit into this hierarchy? (Discussion question — not every real-world model fits neatly into single inheritance.)