Java - Abstract classes

Nothing abstract about abstract classes

Brief

  • When defining an abstract class we need to add the abstract modifier in front of the class keyword.
  • An abstract class can and should be derived as it can't be instantiated.
  • If a class has at least one abstract method, then the class itself must be defined as abstract.
  • An abstract class can have both abstract and concrete methods.
  • A subclass derived from an abstract class must implement all the base class's abstract methods or be defined as abstract itself.

Example

Let's say we want to implement a game that will have different types of players.

Each player will have a name and experience points won throughout the game, but we want different types of players to gain experience in different ways.

This is why, we will define the xp-gaining methods as abstract and let subclasses implement them. So, when a player will win, the number of experience points added will be in line with its type.

public abstract class Player {

    private final String name;
    private Integer xp;

    protected Player(String name) {
        this.name = name;
        this.xp = 0;
    }

    public String getName() {
        return name;
    }

    public Integer getXP() {
        return xp;
    }

    protected abstract Integer getWinXP();

    protected abstract Integer getLossXP();

    protected abstract Integer getTieXP();

    public void win() {
        evolve(getWinXP());
    }

    public void lose() {
        evolve(getLossXP());
    }

    public void tie() {
        evolve(getTieXP());
    }

    private void evolve(Integer newXP) {
        xp += newXP;
    }

}

Extend the Player class with a FootballPlayer class where we implement the abstract classes.

public class FootballPlayer extends Player {
    private static final Integer WIN_XP = 10;
    private static final Integer LOSS_XP = -7;
    private static final Integer TIE_XP = 3;

    public FootballPlayer(String name) {
        super(name);
    }

    @Override
    protected Integer getWinXP() {
        return WIN_XP;
    }

    @Override
    protected Integer getLossXP() {
        return LOSS_XP;
    }

    @Override
    protected Integer getTieXP() {
        return TIE_XP;
    }
}

See it in action:

public class Application {

    public static void main(String[] args) {

        FootballPlayer beckham = new FootballPlayer("David Beckham");
        
        System.out.println("--- before the match ---");
        System.out.println(beckham.getName() + " xp: " + beckham.getXP());
        
        beckham.win();
        System.out.println("--- after the match ---");
        System.out.println(beckham.getName() + " xp: " + beckham.getXP());
    }
}

The result will look like:

--- before the match ---
David Beckham xp: 0
--- after the match ---
David Beckham xp: 10

Bonus

Abstract classes mixed with Generics.

Let's say we want to group our players into teams. In addition to it, we want a team to include the same type of players.

For this to work, we declare a generic type at the class level as T that is of type Player and inside, a list of T type items.

public class Team<T extends Player> {
    private final String name;
    private final List<T> players;

    protected Team(String name) {
        this.name = name;
        this.players = new ArrayList<>();
    }

    public String getName() {
        return this.name;
    }

    public void addPlayer(T player) {
        this.players.add(player);
    }

    public void win() {
        for (T player : players) {
            player.win();
        }
    }

    public void tie() {
        for (T player : players) {
            player.tie();
        }
    }

    public void lose() {
        for (T player : players) {
            player.lose();
        }
    }
}

Now, when we instantiate a team, we have to provide the type of players accepted.

public class Application {

    public static void main(String[] args) {

        Team<FootballPlayer> footballTeam = new Team<>();
        FootballPlayer beckham = new FootballPlayer("David Beckham");
        
        footballTeam.addPlayer(beckham);
        
        System.out.println("--- before the match ---");
        System.out.println(beckham.getName() + " xp: " + beckham.getXP());
        
        footballTeam.win();
        System.out.println("--- after the match ---");
        System.out.println(beckham.getName() + " xp: " + beckham.getXP());
    }
}

The result should look the same:

--- before the match ---
David Beckham xp: 0
--- after the match ---
David Beckham xp: 10

💡
Don't miss out on more posts like this! Susbcribe to our free newsletter!
💡
Currently I am working on a Java Interview e-book designed to successfully get you through any Java technical interview you may take.
Stay tuned! 🚀