Possible Duplicate:
What does Base b2 = new Child(); signify?
I'm a Java beginner. I understand the concepts of class inheritance, but there's one thing I don't quite understand. I'm reading through Java for Dummies, and it's explaining polymorphism. It gives this code as an example:
class Player {
public void move() {...
class BetterPlayer extends Player {
public void move() {...
public class TicTacToeApp {
public static void main(String[] args) {
Player p1 = new Player();
Player p2 = new BetterPlayer();
playTheGame(p1, p2);
}
public static void playTheGame(Player p1, Player p2) {
p1.move();
p2.move();
}
}
Why was p2 created as a Player object? Here's my understanding:
If p2 were a BetterPlayer object (declared like this: BetterPlayer p2 = new BetterPlayer...):
-upcasting is automatic, so it would still work with the playTheGame method
-p2 can be used by any methods requiring a BetterPlayer object
But since it was created as a Player object, now any time p2 is used by a method requiring a BetterPlayer object, it must be explicitly cast to a BetterPlayer object, right? That seems like more work with no benefit, so I'm guessing there has to be some benefit to doing it that way; what is it?
Subclasses have an IS-A relationship to superclasses. BetterPlayer IS-A Player.
For your first question about 'upcasting' your wording is flawed. You can pass p2 into the method because it takes a Player, and BetterPlayer IS-A Player.
For your second question, yes.
For your third question, you are again correct. You can just define p2 as a BetterPlayer to start with though, and then you can use it anywhere you need a Player, and anywhere you need a BetterPlayer.
The advantage is that
p1.move()
andp2.move()
call different functions, withoutplayTheGame
having to do something like this (in pseudocode as my Java is rusty):In this example, there is no advantage.
A more practical example of polymorphism would be an array of players (
Player[]
) or list (List<Player>
) that could contain Players, BetterPlayers, and any other Player subclass.Any Player can, logically speaking, play the game (that's what makes the Player a player); so playTheGame should not care that the Player is a BetterPlayer. The advantage of polymorphism is exactly that playTheGame is not forced to care about something that it shouldn't care about.
We don't cause a real problem by assigning the BetterPlayer to a Player variable (p2) because if we knew we were going to call a bunch of functions that required a BetterPlayer specifically, then we simply wouldn't do that. "Doctor, it hurts when I tell the type system to forget things that actually are important." In the current case, it isn't important, so we have no problem throwing away the specific type information.
But further, even if we declared p2 to be a BetterPlayer, we could still pass it to playTheGame without writing any casts. The upcast when calling the function would be implicit, and inside the function we don't need to downcast because we don't care if we have a BetterPlayer - since we're only using the base Player interface. And then again, a lot of the time (including in the current code) we don't need a variable at all in the first place, so it matters even less what the types are.
playTheGame(new Player(), new BetterPlayer())
works just fine.The key is that a
BetterPlayer
can be passed toplayTheGame()
as if it were aPlayer
. WhenplayTheGame()
callsp2.Move()
, it calls the functionBetterPlayer.move()
, notPlayer.move()
.This means that you can come up with any subclass of
Player
and it will work fine inplayTheGame()
without doing any change whatsoever toplayTheGame()
. This means that you can create whateverPlayer
subclass you like, with whatever.move()
behavior you like, andplayTheGame()
will work fine with it.For one thing, you are having old code call new code without any change in the old code, and this can be important.
For another, you're providing a way to plug in anything that's a
Player
to some pre-existing code, and therefore providing an abstraction barrier.playTheGame()
not only doesn't have to know anything aboutPlayer
, it doesn't have to insist on it being called with aPlayer
.That's good to make maintaining the code easier - in case you need a
StupidPlayer
, you just have to changeto
and it's finished. No need to modify other methods.