Inheritance

Nathan Sprague

Creature Class

We are developing a super-fun text-based video game:

public class Creature
{
    private int xPos;
    private int yPos;
    private int health;

    public Creature(int xPos, int yPos, int health)
    {
        this.xPos = xPos;
        this.yPos = yPos;
        this.health = health;
    }

    public String makeFace()
    {
        return "OvO";
    }
    
    public int getXPos()
    {
        return xPos;
    }

    public int getYPos()
    {
        return yPos;
    }
    
    public void setXPos(int xPos)
    {
        this.xPos = xPos;
    }

    public void setYPos(int yPos)
    {
        this.yPos = yPos;
    }

    public int getHealth()
    {
        return health;
    }

    public void setHealth(int health)
    {
        this.health = health;
    }
}

Monster Class

Of course... the game will need several different creature types. Let's add a monster class with a different appearance:

public class Monster
{
    private int xPos;
    private int yPos;
    private int health;
    private boolean angry;

    public Monster(int xPos, int yPos, int health)
    {
        this.xPos = xPos;
        this.yPos = yPos;
        this.health = health;
        this.angry = false;
    }

    public String makeFace()
    {
        if (angry)
        {
            return ":(>)";
        }
        else {
            return ":)";
        }
    }
    
    public int getXPos()
    {
        return xPos;
    }

    public int getYPos()
    {
        return yPos;
    }
    
    public void setXPos(int xPos)
    {
        this.xPos = xPos;
    }

    public void setYPos(int yPos)
    {
        this.yPos = yPos;
    }

    public int getHealth()
    {
        return health;
    }

    public void setHealth(int health)
    {
        angry = health < 3;
        this.health = health;
    }
}

What's wrong with this class?

What is the solution?

Superclass/Subclass UML

The Worst Thing Possible: Shadowing Instance Variables

public class Monster extends Creature
{
    private int xPos;
    private int yPos;
    private int health;
    private boolean angry;

    public Monster(int xPos, int yPos, int health)
    {
        super(xPos, yPos, health);
        this.xPos = xPos;
        this.yPos = yPos;
        this.health = health;
        this.angry = false;
    }
    
    public String makeFace()
    {
        if (angry)
        {
            return ":(>)" + " " + health;
        }
        else {
            return ":)" + " " + health;
        }
    }   
    
    public void setHealth(int health)
    {
        angry = health < 3;
        this.health = health;
    }
}

What will be printed by this code snippet:

       Monster  monster  = new Monster(50, 50, 10);
        
       System.out.println(monster.getHealth());
       System.out.println(monster.makeFace());
       
       monster.setHealth(1);
        
       System.out.println(monster.getHealth());
       System.out.println(monster.makeFace())
  1. 10
    :) 10
    10
    :(>) 1
  2. 10
    :) 10
    10
    :) 10
  3. 10
    :) 10
    1
    :(>) 1
  4. 10
    :) 10
    1
    :) 10

Monster Code (Without Shadowing)

public class Monster extends Creature
{

    private boolean angry;

    public Monster(int xPos, int yPos, int health)
    {
        super(xPos, yPos, health);
        this.angry = false;
    }
    
    public String makeFace()
    {
        if (angry)
        {
            return ":(>)";
        }
        else {
            return ":)";
        }
    }


    public void setHealth(int health)
    {
      //...
    }

Socrative Quiz: setHealth Method

We want the Monster to become angry when its health falls below 3.

Which implementation works?

  1. public void setHealth(int health)
    {
        angry = health < 3;
        this.health = health;
    }
  2. public void setHealth(int health)
        angry = health < 3;
        setHealth(health);
    }
  3. public void setHealth(int health)
        angry = health < 3;
        super.setHealth(health);
    }
  4. public void setHealth(int health)
        angry = health < 3;
        super.health = health;
    }

Correct Monster Class...

public class Monster extends Creature
{

    private boolean angry;

    public Monster(int xPos, int yPos, int health)
    {
        super(xPos, yPos, health);
        this.angry = false;
    }
    
    public String makeFace()
    {
        if (angry)
        {
            return ":(>)";
        }
        else {
            return ":)";
        }
    }


    public void setHealth(int health)
    {
        angry = health < 3;
        super.setHealth(health);
    }

Alternate Monster Class...

public class Monster extends Creature
{

    private boolean angry;

    public Monster(int xPos, int yPos, int health)
    {
        super(xPos, yPos, health);
        this.angry = false;
    }
    
    public String makeFace()
    {
        if (angry)
        {
            return ":(>)";
        }
        else {
            return ":)";
        }
    }

    public void setHealth(String health)
    {
        super.setHealth(Integer.parseInt(health));
        angry = getHealth() < 3;
    }

What will be printed by the following code snippet?

       Monster  monster  = new Monster(50, 50, 10);
       
       monster.setHealth(1);
       System.out.println(monster.getHealth());
       System.out.println(monster.makeFace());
       
       monster.setHealth("2");
       System.out.println(monster.getHealth());
       System.out.println(monster.makeFace())
  1. 1
    :(>)
    2
    :(>)
  2. 10
    :)
    2
    :(>)
  3. 1
    :)
    1
    :)
  4. 1
    :)
    2
    :(>)

@Override

The problem here is that the author intended to override the setHealth method of the superclass, but overloaded it instead.

This type of error can be avoided by using the @Override annotation. It informs the compiler that you intend a method to override a method defined in the superclass.

This wouldn't compile, because Creature has no method with this signature.

    @Override
    public void setHealth(String health)
    {
        super.setHealth(Integer.parseInt(health));
        angry = getHealth() < 3;
    }

One More Quiz...

Let's add a toString method to Creature:

public class Creature
{
    // Other code not shown.
    
    public String toString()
    {
        return makeFace() + " " + xPos + " " + yPos;
    }
}

What will be printed when this code snippet executes?

Creature c1 = new Creature(20, 30, 10);
Monster m1 = new Monster(15, 17, 8);

System.out.println(c1);
System.out.println(m1);
  1. OvO 20 30
    OvO 15 17
  2. OvO 20 30
    :) 15 17

Answer

B. Calling makeFace on an object of type Monster will always execute the Monster version of the method.