Day 13: Abstract Classes and Interfaces
Abstract classes and interfaces are core tools for object-oriented design. An abstract class is a “partially completed blueprint,” while an interface is a “contract (specification).” Neither can be instantiated directly — child classes must provide concrete implementations.
Abstract Classes
Declared with the abstract keyword, they can contain both abstract methods (methods without a body) and regular methods.
abstract class GameCharacter {
String name;
int hp;
int attackPower;
GameCharacter(String name, int hp, int attackPower) {
this.name = name;
this.hp = hp;
this.attackPower = attackPower;
}
// Abstract method: must be implemented by child classes
abstract void attack(GameCharacter target);
abstract void specialSkill();
// Regular method: common logic
void takeDamage(int damage) {
hp -= damage;
System.out.println(name + " took " + damage + " damage! (HP: " + hp + ")");
if (hp <= 0) {
System.out.println(name + " has fallen!");
}
}
void showStatus() {
System.out.println("[" + name + "] HP: " + hp + " / Attack: " + attackPower);
}
}
class Warrior extends GameCharacter {
Warrior(String name) {
super(name, 200, 30);
}
@Override
void attack(GameCharacter target) {
System.out.println(name + " attacks " + target.name + " with a sword!");
target.takeDamage(attackPower);
}
@Override
void specialSkill() {
System.out.println(name + "'s Fury Strike! (Attack power doubled)");
attackPower *= 2;
}
}
class Mage extends GameCharacter {
int mana;
Mage(String name) {
super(name, 100, 50);
this.mana = 150;
}
@Override
void attack(GameCharacter target) {
if (mana >= 20) {
System.out.println(name + " attacks " + target.name + " with a fireball!");
target.takeDamage(attackPower);
mana -= 20;
} else {
System.out.println("Not enough mana!");
}
}
@Override
void specialSkill() {
System.out.println(name + "'s Meteor Strike!");
mana -= 80;
}
}
public class AbstractClassExample {
public static void main(String[] args) {
// GameCharacter gc = new GameCharacter(...); // Error! Cannot instantiate abstract class
Warrior warrior = new Warrior("Warrior Arthur");
Mage mage = new Mage("Mage Merlin");
warrior.showStatus();
mage.showStatus();
warrior.attack(mage);
mage.specialSkill();
mage.attack(warrior);
}
}
Interfaces
Interfaces define the method specifications that a class must implement. Use implements to implement them, and multiple implementations are allowed.
interface Flyable {
void fly();
int getMaxAltitude();
}
interface Swimmable {
void swim();
int getMaxDepth();
}
interface Runnable {
void run();
int getMaxSpeed();
}
// Multiple interface implementation
class Duck implements Flyable, Swimmable, Runnable {
String name;
Duck(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println(name + " flies into the sky!");
}
@Override
public int getMaxAltitude() {
return 500;
}
@Override
public void swim() {
System.out.println(name + " swims on the water!");
}
@Override
public int getMaxDepth() {
return 2;
}
@Override
public void run() {
System.out.println(name + " waddles along!");
}
@Override
public int getMaxSpeed() {
return 10;
}
}
class Penguin implements Swimmable, Runnable {
String name;
Penguin(String name) {
this.name = name;
}
@Override
public void swim() {
System.out.println(name + " swims rapidly!");
}
@Override
public int getMaxDepth() {
return 100;
}
@Override
public void run() {
System.out.println(name + " waddles quickly!");
}
@Override
public int getMaxSpeed() {
return 5;
}
}
public class InterfaceExample {
// Declare parameters as interface types (polymorphism)
static void letItFly(Flyable f) {
f.fly();
System.out.println("Max altitude: " + f.getMaxAltitude() + "m");
}
static void letItSwim(Swimmable s) {
s.swim();
System.out.println("Max depth: " + s.getMaxDepth() + "m");
}
public static void main(String[] args) {
Duck duck = new Duck("Donald");
Penguin penguin = new Penguin("Pororo");
letItFly(duck);
letItSwim(duck);
letItSwim(penguin);
// letItFly(penguin); // Compile error! Penguin is not Flyable
}
}
Default Methods and Static Methods
Since Java 8, interfaces can also define methods with implementations.
interface Logger {
// Abstract method
void log(String message);
// Default method: provides a default implementation
default void info(String message) {
log("[INFO] " + message);
}
default void error(String message) {
log("[ERROR] " + message);
}
default void warn(String message) {
log("[WARN] " + message);
}
// Static method: called using the interface name
static Logger consoleLogger() {
return message -> System.out.println(message);
}
}
class FileLogger implements Logger {
@Override
public void log(String message) {
// Instead of writing to a file, we display on console here
System.out.println("[FILE] " + message);
}
// Default methods can be overridden if needed
@Override
public void error(String message) {
log("[CRITICAL ERROR] " + message);
}
}
public class DefaultMethodExample {
public static void main(String[] args) {
Logger console = Logger.consoleLogger();
console.info("Server started");
console.error("Connection failed");
console.warn("Low memory");
Logger fileLogger = new FileLogger();
fileLogger.info("Request processed");
fileLogger.error("Disk space low"); // Overridden version
}
}
Abstract Class vs Interface Comparison
// Abstract class: "is-a" relationship
// - Can have state (fields)
// - Can have constructors
// - Only single inheritance
// - Use when you need common logic + enforced abstract methods
abstract class Vehicle {
int speed;
abstract void move();
void stop() { speed = 0; }
}
// Interface: "can-do" relationship
// - Cannot have state (only constants)
// - No constructors
// - Multiple implementation allowed
// - Use when defining capabilities (roles)
interface Chargeable {
void charge();
int getBatteryLevel();
}
interface GPSEnabled {
String getCurrentLocation();
}
// Combination: abstract class inheritance + multiple interface implementation
class ElectricScooter extends Vehicle implements Chargeable, GPSEnabled {
int battery = 100;
@Override
void move() { System.out.println("The electric scooter rides!"); }
@Override
public void charge() { battery = 100; }
@Override
public int getBatteryLevel() { return battery; }
@Override
public String getCurrentLocation() { return "Seoul, Gangnam-gu"; }
}
Today’s Exercises
-
Payment System Design: Define a
Payableinterface (methods:pay(long amount),refund(long amount)) and implement it withCreditCard,BankAccount, andCryptoCurrencyclasses. -
Sortable Collection: Create a
Studentclass that implements theComparable<T>interface. It has name and score fields, and sorts in descending order by score. Test withArrays.sort(). -
Game Character System: Combine an abstract class
Character(name, HP) with interfacesHealableandBuffable.Paladinimplements both interfaces, whileRogueimplements onlyBuffable.