This module covers advanced OOP concepts in Java, focusing on interfaces, abstract classes, and their roles in abstraction and polymorphism. Designed for beginners building on Weeks 1-10 of the 3-month Java fundamentals plan, it includes detailed explanations, multiple code examples per concept, and practice exercises for each day of Week 11. The module concludes with a console-based Shape Drawing System mini-project to apply these concepts.
Objective: Learn to define and implement interfaces for abstraction and polymorphism.
- Interface: A contract specifying methods that a class must implement, without providing implementation details.
- Syntax:
interface InterfaceName { returnType methodName(parameters); }
- Classes implement interfaces using
implements. - All interface methods are implicitly
publicandabstract(unless default/static).
- Syntax:
- Use interfaces to achieve polymorphism and define common behavior across unrelated classes.
- Requires
implements InterfaceNamein the class declaration.
Example 1: Basic Interface
interface Printable {
void print();
}
public class Document implements Printable {
private String title;
Document(String title) {
this.title = title;
}
@Override
public void print() {
System.out.println("Printing document: " + title);
}
public static void main(String[] args) {
Document doc = new Document("Report");
doc.print();
}
}Example 2: Multiple Classes Implementing Interface
interface Playable {
void play();
}
class Guitar implements Playable {
@Override
public void play() {
System.out.println("Playing guitar");
}
}
public class Piano implements Playable {
@Override
public void play() {
System.out.println("Playing piano");
}
public static void main(String[] args) {
Playable guitar = new Guitar();
Playable piano = new Piano();
guitar.play();
piano.play();
}
}Example 3: Interface with Multiple Methods
interface Vehicle {
void start();
void stop();
}
public class Car implements Vehicle {
private String model;
Car(String model) {
this.model = model;
}
@Override
public void start() {
System.out.println(model + " is starting");
}
@Override
public void stop() {
System.out.println(model + " is stopping");
}
public static void main(String[] args) {
Car car = new Car("Toyota");
car.start();
car.stop();
}
}- Create an interface
Drawablewith adrawmethod; implement it in aCircleclass. - Write two classes (
BookandPoster) that implement aDisplayableinterface with adisplaymethod. - Create an interface with two methods and implement it in a class.
Practice Code:
interface Drawable {
void draw();
}
public class CirclePractice implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
public static void main(String[] args) {
CirclePractice circle = new CirclePractice();
circle.draw();
}
}Objective: Use abstract classes for partial implementation and inheritance.
- Abstract Class: A class that cannot be instantiated and may contain abstract (unimplemented) and concrete (implemented) methods.
- Syntax:
abstract class ClassName { abstract void methodName(); void concreteMethod() { // Implementation } }
- Declared with
abstractkeyword; subclasses extend usingextends.
- Syntax:
- Use for shared code among related classes, unlike interfaces which are for unrelated classes.
- Subclasses must implement all abstract methods or be abstract themselves.
Example 1: Basic Abstract Class
abstract class Animal {
abstract void makeSound();
void sleep() {
System.out.println("Animal is sleeping");
}
}
public class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Woof");
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound();
dog.sleep();
}
}Example 2: Abstract Class with Constructor
abstract class Shape {
protected String color;
Shape(String color) {
this.color = color;
}
abstract double getArea();
}
public class Rectangle extends Shape {
private double length, width;
Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
double getArea() {
return length * width;
}
public static void main(String[] args) {
Rectangle rect = new Rectangle("Blue", 4.0, 3.0);
System.out.println("Area: " + rect.getArea() + ", Color: " + rect.color);
}
}Example 3: Multiple Subclasses
abstract class Vehicle {
protected String brand;
Vehicle(String brand) {
this.brand = brand;
}
abstract void move();
}
class Bike extends Vehicle {
Bike(String brand) {
super(brand);
}
@Override
void move() {
System.out.println(brand + " bike is moving");
}
}
public class Truck extends Vehicle {
Truck(String brand) {
super(brand);
}
@Override
void move() {
System.out.println(brand + " truck is moving");
}
public static void main(String[] args) {
Vehicle bike = new Bike("Yamaha");
Vehicle truck = new Truck("Ford");
bike.move();
truck.move();
}
}- Create an abstract class
Shapewith an abstractgetPerimetermethod; implement inTriangle. - Write an abstract class with a constructor and a concrete method; extend it.
- Create two subclasses of an abstract
Employeeclass with an abstractworkmethod.
Practice Code:
abstract class Shape {
abstract double getPerimeter();
}
public class TrianglePractice extends Shape {
private double a, b, c;
TrianglePractice(double a, double b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
double getPerimeter() {
return a + b + c;
}
public static void main(String[] args) {
TrianglePractice triangle = new TrianglePractice(3, 4, 5);
System.out.println("Perimeter: " + triangle.getPerimeter());
}
}Objective: Understand when to use interfaces versus abstract classes.
- Interfaces:
- For unrelated classes sharing common behavior (e.g.,
DrawableforCircleandText). - Support multiple implementations (a class can implement multiple interfaces).
- No fields or constructors; only method signatures (or default/static methods).
- For unrelated classes sharing common behavior (e.g.,
- Abstract Classes:
- For related classes sharing code and state (e.g.,
VehicleforCarandTruck). - Can have fields, constructors, and concrete methods.
- Single inheritance (a class can extend only one class).
- For related classes sharing code and state (e.g.,
- Use interfaces for flexibility, abstract classes for shared implementation.
Example 1: Interface for Unrelated Classes
interface Displayable {
void display();
}
class Image implements Displayable {
@Override
public void display() {
System.out.println("Displaying image");
}
}
public class Text implements Displayable {
@Override
public void display() {
System.out.println("Displaying text");
}
public static void main(String[] args) {
Displayable image = new Image();
Displayable text = new Text();
image.display();
text.display();
}
}Example 2: Abstract Class for Related Classes
abstract class Animal {
protected String name;
Animal(String name) {
this.name = name;
}
abstract void eat();
}
public class Cat extends Animal {
Cat(String name) {
super(name);
}
@Override
void eat() {
System.out.println(name + " eats fish");
}
public static void main(String[] args) {
Cat cat = new Cat("Whiskers");
cat.eat();
}
}Example 3: Combining Interface and Abstract Class
interface Movable {
void move();
}
abstract class Vehicle {
protected String brand;
Vehicle(String brand) {
this.brand = brand;
}
abstract void start();
}
public class Motorcycle extends Vehicle implements Movable {
Motorcycle(String brand) {
super(brand);
}
@Override
void start() {
System.out.println(brand + " motorcycle starts");
}
@Override
public void move() {
System.out.println(brand + " motorcycle moves");
}
public static void main(String[] args) {
Motorcycle moto = new Motorcycle("Honda");
moto.start();
moto.move();
}
}- Create an interface
Resizableand implement it in aWindowclass. - Write an abstract class
Devicewith a concrete method; extend it inPhone. - Combine an interface and abstract class in a program (e.g.,
VehicleandMovable).
Practice Code:
interface Resizable {
void resize();
}
abstract class Device {
protected String type;
Device(String type) {
this.type = type;
}
void powerOn() {
System.out.println(type + " powered on");
}
}
public class PhonePractice extends Device implements Resizable {
PhonePractice(String type) {
super(type);
}
@Override
public void resize() {
System.out.println(type + " resized");
}
public static void main(String[] args) {
PhonePractice phone = new PhonePractice("Smartphone");
phone.powerOn();
phone.resize();
}
}Objective: Use interfaces and abstract classes for polymorphic behavior.
- Polymorphism: Treating objects of different classes as instances of a common interface or superclass.
- Use interface or abstract class references to call overridden methods.
- Enables flexible and reusable code.
Example 1: Polymorphism with Interface
interface Drivable {
void drive();
}
class Car implements Drivable {
@Override
public void drive() {
System.out.println("Driving car");
}
}
public class Truck implements Drivable {
@Override
public void drive() {
System.out.println("Driving truck");
}
public static void main(String[] args) {
Drivable car = new Car();
Drivable truck = new Truck();
car.drive();
truck.drive();
}
}Example 2: Polymorphism with Abstract Class
abstract class Shape {
abstract double getArea();
}
class Circle extends Shape {
private double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
}
public class Square extends Shape {
private double side;
Square(double side) {
this.side = side;
}
@Override
double getArea() {
return side * side;
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape square = new Square(4.0);
System.out.println("Circle Area: " + circle.getArea());
System.out.println("Square Area: " + square.getArea());
}
}Example 3: Combining Interface and Abstract Class
interface Drawable {
void draw();
}
abstract class Shape {
protected String name;
Shape(String name) {
this.name = name;
}
abstract void describe();
}
public class Rectangle extends Shape implements Drawable {
Rectangle(String name) {
super(name);
}
@Override
public void draw() {
System.out.println("Drawing " + name);
}
@Override
void describe() {
System.out.println("This is a " + name);
}
public static void main(String[] args) {
Shape rect = new Rectangle("Rectangle");
Drawable drawable = (Drawable) rect;
rect.describe();
drawable.draw();
}
}- Create an interface
Runnableand use it polymorphically with two classes. - Write an abstract class
Animaland use it polymorphically withDogandCat. - Combine an interface and abstract class in a polymorphic program.
Practice Code:
interface Runnable {
void run();
}
abstract class Animal {
abstract void makeSound();
}
public class DogPractice extends Animal implements Runnable {
@Override
public void run() {
System.out.println("Dog is running");
}
@Override
void makeSound() {
System.out.println("Dog barks");
}
public static void main(String[] args) {
Animal dog = new DogPractice();
Runnable runner = (Runnable) dog;
dog.makeSound();
runner.run();
}
}Objective: Solidify interfaces, abstract classes, and polymorphic design.
- Review best practices:
- Use interfaces for defining contracts across unrelated classes.
- Use abstract classes for shared code in related classes.
- Leverage polymorphism for flexible code.
- Combine with Weeks 1-10 concepts (OOP, collections, file I/O).
- Design tip: Choose interfaces for flexibility, abstract classes for shared implementation.
Example 1: Interface-Based Design
interface Payable {
double calculatePay();
}
class Employee implements Payable {
private double salary;
Employee(double salary) {
this.salary = salary;
}
@Override
public double calculatePay() {
return salary;
}
}
public class Contractor implements Payable {
private double hours;
private double rate;
Contractor(double hours, double rate) {
this.hours = hours;
this.rate = rate;
}
@Override
public double calculatePay() {
return hours * rate;
}
public static void main(String[] args) {
Payable emp = new Employee(5000);
Payable contractor = new Contractor(40, 50);
System.out.println("Employee Pay: $" + emp.calculatePay());
System.out.println("Contractor Pay: $" + contractor.calculatePay());
}
}Example 2: Abstract Class Design
abstract class Product {
protected String name;
protected double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
abstract void display();
}
public class Book extends Product {
Book(String name, double price) {
super(name, price);
}
@Override
void display() {
System.out.println("Book: " + name + ", Price: $" + price);
}
public static void main(String[] args) {
Product book = new Book("Java Guide", 29.99);
book.display();
}
}Example 3: Combined Design
interface Repairable {
void repair();
}
abstract class Device {
protected String brand;
Device(String brand) {
this.brand = brand;
}
abstract void powerOn();
}
public class Laptop extends Device implements Repairable {
Laptop(String brand) {
super(brand);
}
@Override
void powerOn() {
System.out.println(brand + " laptop powers on");
}
@Override
public void repair() {
System.out.println(brand + " laptop repaired");
}
public static void main(String[] args) {
Device laptop = new Laptop("Dell");
Repairable repairable = (Repairable) laptop;
laptop.powerOn();
repairable.repair();
}
}- Create an interface
Calculablefor calculating values; implement in two classes. - Write an abstract class
Vehiclewith a concrete method; extend in two subclasses. - Combine an interface and abstract class in a program with polymorphism.
Practice Code:
interface Calculable {
double calculate();
}
abstract class Vehicle {
protected String type;
Vehicle(String type) {
this.type = type;
}
void start() {
System.out.println(type + " starts");
}
}
public class CarPractice extends Vehicle implements Calculable {
private double speed;
CarPractice(String type, double speed) {
super(type);
this.speed = speed;
}
@Override
public double calculate() {
return speed * 1.5; // Example calculation
}
public static void main(String[] args) {
Vehicle car = new CarPractice("Car", 60.0);
Calculable calc = (Calculable) car;
car.start();
System.out.println("Calculated value: " + calc.calculate());
}
}Objective: Consolidate Week 11 learning with a Shape Drawing System.
- Revisit:
- Interfaces for defining contracts and enabling polymorphism.
- Abstract classes for shared code and partial implementation.
- Polymorphism for flexible designs.
- Combining interfaces and abstract classes.
- Debug common errors (e.g., missing method implementations, incorrect casting).
Create a console-based program that:
- Defines an interface
Drawablewith adrawmethod. - Creates an abstract class
Shapewith fieldscolorand abstract methodgetArea. - Implements
Drawableand extendsShapeinCircleandRectangleclasses. - Uses a
HashMapto store shapes (name as key,Shapeobject as value). - Demonstrates polymorphism by calling
drawandgetArea.
Project Code:
import java.util.HashMap;
interface Drawable {
void draw();
}
abstract class Shape {
protected String color;
Shape(String color) {
this.color = color;
}
abstract double getArea();
}
class Circle extends Shape implements Drawable {
private double radius;
Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("Drawing Circle, Color: " + color + ", Area: " + getArea());
}
}
class Rectangle extends Shape implements Drawable {
private double length, width;
Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
double getArea() {
return length * width;
}
@Override
public void draw() {
System.out.println("Drawing Rectangle, Color: " + color + ", Area: " + getArea());
}
}
public class ShapeDrawingSystem {
private HashMap<String, Shape> shapes;
ShapeDrawingSystem() {
shapes = new HashMap<>();
}
void addShape(String name, Shape shape) {
shapes.put(name, shape);
}
void drawAll() {
for (Shape shape : shapes.values()) {
if (shape instanceof Drawable) {
((Drawable) shape).draw();
}
}
}
public static void main(String[] args) {
ShapeDrawingSystem system = new ShapeDrawingSystem();
system.addShape("Circle1", new Circle("Red", 5.0));
system.addShape("Rect1", new Rectangle("Blue", 4.0, 3.0));
system.drawAll();
}
}- Run and test the system with different shapes.
- Add a
Triangleclass implementingDrawableand extendingShape. - Extend the program to calculate the total area of all shapes.
Extended Practice Code (with total area):
import java.util.HashMap;
interface Drawable {
void draw();
}
abstract class Shape {
protected String color;
Shape(String color) {
this.color = color;
}
abstract double getArea();
}
class Circle extends Shape implements Drawable {
private double radius;
Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("Drawing Circle, Color: " + color + ", Area: " + getArea());
}
}
public class ExtendedShapeDrawingSystem {
private HashMap<String, Shape> shapes;
ExtendedShapeDrawingSystem() {
shapes = new HashMap<>();
}
void addShape(String name, Shape shape) {
shapes.put(name, shape);
}
double getTotalArea() {
double total = 0;
for (Shape shape : shapes.values()) {
total += shape.getArea();
}
return total;
}
public static void main(String[] args) {
ExtendedShapeDrawingSystem system = new ExtendedShapeDrawingSystem();
system.addShape("Circle1", new Circle("Red", 5.0));
System.out.println("Total Area: " + system.getTotalArea());
}
}- Practice: Test each code example in an IDE or terminal.
- Debug: Check for missing interface implementations or abstract method overrides.
- Resources: Refer to Oracle’s Java Tutorials or W3Schools for interfaces and abstract classes.
- Experiment: Modify interfaces or add methods to see polymorphic behavior.