Java - Abstract classes
Nothing abstract about abstract classes
Brief
- When defining an abstract class we need to add the
abstract
modifier in front of theclass
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
Stay tuned! 🚀