Skip to main content
  1. Resources/
  2. Study Materials/
  3. Information & Communication Technology Engineering/
  4. ICT Semester 4/
  5. Java Programming (4343203)/

24 mins· ·
Milav Dabgar
Author
Milav Dabgar
Experienced lecturer in the electrical and electronic manufacturing industry. Skilled in Embedded Systems, Image Processing, Data Science, MATLAB, Python, STM32. Strong education professional with a Master’s degree in Communication Systems Engineering from L.D. College of Engineering - Ahmedabad.
Lecture 12 - Interfaces and Multiple Inheritance

Java Programming

Lecture 12: Interfaces and Multiple Inheritance

Course: 4343203 - Java Programming

GTU Semester 4 | Unit 2


Learning Objectives:

  • Master interface concepts and implementation
  • Understand multiple inheritance through interfaces
  • Learn default and static methods in interfaces
  • Apply interface-based design patterns
  • Compare interfaces with abstract classes

Understanding Interfaces

Interface is a contract that defines what a class can do, without specifying how it does it. It's a completely abstract class that contains only abstract methods (until Java 8) and constants.

Interface Characteristics:

  • 100% Abstract: All methods are implicitly abstract
  • Constants Only: Variables are public static final
  • Multiple Inheritance: Class can implement multiple interfaces
  • No Constructors: Cannot be instantiated
  • Implementation Contract: Implementing class must define all methods

Interface Benefits:

  • Achieves multiple inheritance
  • Promotes loose coupling
  • Supports design by contract
  • Enables polymorphism
  • Facilitates testing (mocking)

Basic Interface Syntax:


// Interface declaration
public interface Drawable {
    // Constant (implicitly public static final)
    int MAX_SIZE = 1000;
    
    // Abstract methods (implicitly public abstract)
    void draw();
    void resize(int width, int height);
    String getType();
}

// Interface implementation
public class Circle implements Drawable {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    // Must implement all interface methods
    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius " + radius);
    }
    
    @Override
    public void resize(int width, int height) {
        this.radius = Math.min(width, height) / 2.0;
        System.out.println("Circle resized to radius " + radius);
    }
    
    @Override
    public String getType() {
        return "Circle";
    }
}

Multiple Inheritance through Interfaces

Multiple Interface Implementation:


// Interface 1
interface Flyable {
    void fly();
    double getMaxAltitude();
}

// Interface 2
interface Swimmable {
    void swim();
    double getMaxDepth();
}

// Interface 3
interface Walkable {
    void walk();
    double getMaxSpeed();
}

// Class implementing multiple interfaces
class Duck implements Flyable, Swimmable, Walkable {
    private String name;
    
    public Duck(String name) {
        this.name = name;
    }
    
    // Implement Flyable
    @Override
    public void fly() {
        System.out.println(name + " is flying in the sky");
    }
    
    @Override
    public double getMaxAltitude() {
        return 1000.0; // meters
    }
    
    // Implement Swimmable
    @Override
    public void swim() {
        System.out.println(name + " is swimming in water");
    }
    
    @Override
    public double getMaxDepth() {
        return 2.0; // meters
    }
    
    // Implement Walkable
    @Override
    public void walk() {
        System.out.println(name + " is walking on land");
    }
    
    @Override
    public double getMaxSpeed() {
        return 5.0; // km/h
    }
}

Using Multiple Interfaces:


public class MultipleInheritanceDemo {
    public static void main(String[] args) {
        Duck duck = new Duck("Donald");
        
        // Duck can be treated as any of its interfaces
        Flyable flyer = duck;
        Swimmable swimmer = duck;
        Walkable walker = duck;
        
        // Polymorphic behavior
        demonstrateFlight(flyer);
        demonstrateSwimming(swimmer);
        demonstrateWalking(walker);
        
        // Direct usage
        duck.fly();
        duck.swim();
        duck.walk();
        
        System.out.println("Max altitude: " + duck.getMaxAltitude() + "m");
        System.out.println("Max depth: " + duck.getMaxDepth() + "m");
        System.out.println("Max speed: " + duck.getMaxSpeed() + " km/h");
    }
    
    public static void demonstrateFlight(Flyable flyer) {
        System.out.println("Testing flight capability:");
        flyer.fly();
    }
    
    public static void demonstrateSwimming(Swimmable swimmer) {
        System.out.println("Testing swimming capability:");
        swimmer.swim();
    }
    
    public static void demonstrateWalking(Walkable walker) {
        System.out.println("Testing walking capability:");
        walker.walk();
    }
}

Multiple Inheritance Diamond Problem: Java interfaces solve the diamond problem because they only contain method signatures (until Java 8). If a class implements multiple interfaces with same method signature, only one implementation is needed.

Interface Inheritance

Interface Extending Interface:


// Base interface
interface Vehicle {
    void start();
    void stop();
    double getFuelEfficiency();
}

// Extended interface
interface ElectricVehicle extends Vehicle {
    void charge();
    double getBatteryCapacity();
    int getRange();
}

// Multiple interface inheritance
interface FlyingCar extends Vehicle, Flyable {
    void takeOff();
    void land();
    double getFlightRange();
}

// Implementation
class Tesla implements ElectricVehicle {
    private String model;
    private double batteryCapacity;
    private boolean isRunning;
    
    public Tesla(String model, double batteryCapacity) {
        this.model = model;
        this.batteryCapacity = batteryCapacity;
        this.isRunning = false;
    }
    
    // Implement Vehicle methods
    @Override
    public void start() {
        isRunning = true;
        System.out.println(model + " started silently");
    }
    
    @Override
    public void stop() {
        isRunning = false;
        System.out.println(model + " stopped");
    }
    
    @Override
    public double getFuelEfficiency() {
        return 0.0; // Electric - no fuel
    }
    
    // Implement ElectricVehicle methods
    @Override
    public void charge() {
        System.out.println(model + " is charging...");
    }
    
    @Override
    public double getBatteryCapacity() {
        return batteryCapacity;
    }
    
    @Override
    public int getRange() {
        return (int)(batteryCapacity * 3.5); // km per kWh
    }
}

Complex Implementation:


class FlyingCarPrototype implements FlyingCar {
    private String name;
    private boolean isFlying;
    private boolean isRunning;
    
    public FlyingCarPrototype(String name) {
        this.name = name;
        this.isFlying = false;
        this.isRunning = false;
    }
    
    // Vehicle methods
    @Override
    public void start() {
        isRunning = true;
        System.out.println(name + " engine started");
    }
    
    @Override
    public void stop() {
        isRunning = false;
        isFlying = false;
        System.out.println(name + " stopped");
    }
    
    @Override
    public double getFuelEfficiency() {
        return isFlying ? 8.0 : 15.0; // km/l
    }
    
    // Flyable methods
    @Override
    public void fly() {
        if (isRunning) {
            isFlying = true;
            System.out.println(name + " is flying!");
        }
    }
    
    @Override
    public double getMaxAltitude() {
        return 3000.0; // meters
    }
    
    // FlyingCar specific methods
    @Override
    public void takeOff() {
        System.out.println(name + " taking off...");
        fly();
    }
    
    @Override
    public void land() {
        isFlying = false;
        System.out.println(name + " landed safely");
    }
    
    @Override
    public double getFlightRange() {
        return 500.0; // km
    }
}

Default and Static Methods in Interfaces

Default Methods:

Java 8 introduced default methods to provide implementation in interfaces without breaking existing implementations.


interface PaymentProcessor {
    // Abstract method
    void processPayment(double amount);
    
    // Default method with implementation
    default void sendConfirmation(String email) {
        System.out.println("Sending confirmation to: " + email);
        System.out.println("Payment processed successfully!");
    }
    
    // Default method
    default void logTransaction(double amount) {
        System.out.println("Transaction logged: $" + amount);
        System.out.println("Timestamp: " + java.time.LocalDateTime.now());
    }
    
    // Static method
    static boolean validateAmount(double amount) {
        return amount > 0 && amount <= 10000;
    }
    
    // Static utility method
    static String formatCurrency(double amount) {
        return String.format("$%.2f", amount);
    }
}

class CreditCardProcessor implements PaymentProcessor {
    private String cardNumber;
    
    public CreditCardProcessor(String cardNumber) {
        this.cardNumber = cardNumber;
    }
    
    @Override
    public void processPayment(double amount) {
        if (PaymentProcessor.validateAmount(amount)) {
            System.out.println("Processing credit card payment: " + 
                              PaymentProcessor.formatCurrency(amount));
            System.out.println("Card ending in: " + 
                              cardNumber.substring(cardNumber.length() - 4));
        } else {
            System.out.println("Invalid payment amount!");
        }
    }
    
    // Can override default method if needed
    @Override
    public void sendConfirmation(String email) {
        System.out.println("Credit Card Payment Confirmation");
        System.out.println("Email: " + email);
        System.out.println("Card used: ****" + cardNumber.substring(cardNumber.length() - 4));
    }
}

Multiple Default Methods:


interface Logger {
    default void log(String message) {
        System.out.println("LOG: " + message);
    }
}

interface Auditor {
    default void log(String message) {
        System.out.println("AUDIT: " + message);
    }
}

// Class implementing both interfaces with same default method
class DatabaseService implements Logger, Auditor {
    
    // Must override to resolve conflict
    @Override
    public void log(String message) {
        System.out.println("DATABASE: " + message);
        // Can call specific default methods
        Logger.super.log(message);  // Call Logger's default
        Auditor.super.log(message); // Call Auditor's default
    }
    
    public void saveData(String data) {
        log("Saving data: " + data);
    }
}

// Usage example
class DefaultMethodDemo {
    public static void main(String[] args) {
        CreditCardProcessor processor = 
            new CreditCardProcessor("1234567890123456");
        
        // Use implemented method
        processor.processPayment(150.75);
        
        // Use default methods
        processor.sendConfirmation("user@example.com");
        processor.logTransaction(150.75);
        
        // Use static methods
        System.out.println("Amount valid: " + 
                          PaymentProcessor.validateAmount(150.75));
        System.out.println("Formatted: " + 
                          PaymentProcessor.formatCurrency(150.75));
        
        // Demonstrate multiple default method resolution
        DatabaseService dbService = new DatabaseService();
        dbService.saveData("User information");
    }
}

Interface vs Abstract Class

FeatureInterfaceAbstract Class
Keywordinterfaceabstract class
InheritanceMultiple (implements)Single (extends)
MethodsAbstract, default, staticAbstract, concrete
Variablespublic static final onlyAny type
ConstructorNo constructorsCan have constructors
Access Modifierspublic (implicitly)Any access modifier
Implementation0% to partial (Java 8+)0% to 99%

When to Use Interface:

  • Multiple inheritance needed
  • Contract definition (what to do)
  • Loose coupling required
  • No state to maintain
  • Plugin architecture

Interface Example:


interface Sortable {
    void sort();
    default void shuffle() {
        System.out.println("Shuffling elements");
    }
}

interface Searchable {
    boolean search(Object item);
}

class ArrayList implements Sortable, Searchable {
    // Implementation for multiple contracts
}

When to Use Abstract Class:

  • Common implementation needed
  • State to be maintained
  • Constructor required
  • Code reuse priority
  • Related classes hierarchy

Abstract Class Example:


abstract class Animal {
    protected String name; // State
    
    public Animal(String name) { // Constructor
        this.name = name;
    }
    
    public void sleep() { // Common implementation
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound(); // Must implement
}

Functional Interfaces

What are Functional Interfaces?

A functional interface has exactly one abstract method. They can be used with lambda expressions and method references.


@FunctionalInterface
interface Calculator {
    double calculate(double a, double b);
    
    // Default methods allowed
    default void printResult(double result) {
        System.out.println("Result: " + result);
    }
    
    // Static methods allowed
    static void printWelcome() {
        System.out.println("Welcome to Calculator!");
    }
}

// Implementation using lambda expression
public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Lambda expressions
        Calculator add = (a, b) -> a + b;
        Calculator multiply = (a, b) -> a * b;
        Calculator divide = (a, b) -> b != 0 ? a / b : 0;
        
        // Usage
        double result1 = add.calculate(10, 5);
        double result2 = multiply.calculate(10, 5);
        double result3 = divide.calculate(10, 5);
        
        add.printResult(result1); // 15.0
        multiply.printResult(result2); // 50.0
        divide.printResult(result3); // 2.0
        
        Calculator.printWelcome();
    }
}

Built-in Functional Interfaces:


import java.util.function.*;
import java.util.Arrays;
import java.util.List;

public class BuiltInFunctionalInterfaces {
    public static void main(String[] args) {
        List names = Arrays.asList("Alice", "Bob", "Charlie");
        
        // Predicate - takes T, returns boolean
        Predicate startsWithA = name -> name.startsWith("A");
        names.stream().filter(startsWithA).forEach(System.out::println);
        
        // Function - takes T, returns R
        Function getLength = String::length;
        names.stream().map(getLength).forEach(System.out::println);
        
        // Consumer - takes T, returns void
        Consumer printUpperCase = name -> 
            System.out.println(name.toUpperCase());
        names.forEach(printUpperCase);
        
        // Supplier - takes nothing, returns T
        Supplier getRandomName = () -> 
            names.get((int)(Math.random() * names.size()));
        System.out.println("Random name: " + getRandomName.get());
        
        // BiFunction - takes T and U, returns R
        BiFunction max = Integer::max;
        System.out.println("Max: " + max.apply(10, 20));
    }
}

Real-world Interface Example: Plugin System

Plugin Architecture:


// Base plugin interface
interface Plugin {
    String getName();
    String getVersion();
    void initialize();
    void execute();
    void cleanup();
    
    // Default method for common functionality
    default void logActivity(String message) {
        System.out.println("[" + getName() + " v" + getVersion() + "] " + message);
    }
}

// Database plugin interface
interface DatabasePlugin extends Plugin {
    void connect(String connectionString);
    void disconnect();
    void executeQuery(String query);
}

// File processing plugin interface
interface FilePlugin extends Plugin {
    void processFile(String filePath);
    String[] getSupportedFormats();
}

// Concrete implementations
class MySQLPlugin implements DatabasePlugin {
    private String name = "MySQL Connector";
    private String version = "1.0.0";
    private boolean connected = false;
    
    @Override
    public String getName() { return name; }
    
    @Override
    public String getVersion() { return version; }
    
    @Override
    public void initialize() {
        logActivity("Initializing MySQL plugin...");
    }
    
    @Override
    public void execute() {
        logActivity("MySQL plugin ready for database operations");
    }
    
    @Override
    public void cleanup() {
        disconnect();
        logActivity("MySQL plugin cleanup completed");
    }
    
    @Override
    public void connect(String connectionString) {
        connected = true;
        logActivity("Connected to: " + connectionString);
    }
    
    @Override
    public void disconnect() {
        connected = false;
        logActivity("Disconnected from database");
    }
    
    @Override
    public void executeQuery(String query) {
        if (connected) {
            logActivity("Executing query: " + query);
        } else {
            logActivity("Error: Not connected to database");
        }
    }
}

Plugin Manager:


class PDFPlugin implements FilePlugin {
    @Override
    public String getName() { return "PDF Processor"; }
    
    @Override
    public String getVersion() { return "2.1.0"; }
    
    @Override
    public void initialize() {
        logActivity("PDF plugin initialized");
    }
    
    @Override
    public void execute() {
        logActivity("PDF plugin ready");
    }
    
    @Override
    public void cleanup() {
        logActivity("PDF plugin cleanup completed");
    }
    
    @Override
    public void processFile(String filePath) {
        logActivity("Processing PDF file: " + filePath);
    }
    
    @Override
    public String[] getSupportedFormats() {
        return new String[]{"pdf", "PDF"};
    }
}

// Plugin manager
class PluginManager {
    private List plugins = new ArrayList<>();
    
    public void registerPlugin(Plugin plugin) {
        plugins.add(plugin);
        plugin.initialize();
        System.out.println("Plugin registered: " + plugin.getName());
    }
    
    public void executeAllPlugins() {
        System.out.println("\n=== Executing All Plugins ===");
        for (Plugin plugin : plugins) {
            plugin.execute();
        }
    }
    
    public void shutdownAllPlugins() {
        System.out.println("\n=== Shutting Down Plugins ===");
        for (Plugin plugin : plugins) {
            plugin.cleanup();
        }
    }
    
    public Plugin findPlugin(String name) {
        return plugins.stream()
                     .filter(p -> p.getName().equals(name))
                     .findFirst()
                     .orElse(null);
    }
}

// Demo
public class PluginSystemDemo {
    public static void main(String[] args) {
        PluginManager manager = new PluginManager();
        
        // Register plugins
        manager.registerPlugin(new MySQLPlugin());
        manager.registerPlugin(new PDFPlugin());
        
        // Execute plugins
        manager.executeAllPlugins();
        
        // Use specific plugin
        DatabasePlugin dbPlugin = (DatabasePlugin) manager.findPlugin("MySQL Connector");
        if (dbPlugin != null) {
            dbPlugin.connect("jdbc:mysql://localhost:3306/mydb");
            dbPlugin.executeQuery("SELECT * FROM users");
        }
        
        // Shutdown
        manager.shutdownAllPlugins();
    }
}

Previous Year Exam Questions

Q1. (GTU Summer 2022) What are interfaces in Java? Explain how Java achieves multiple inheritance through interfaces with a practical example.

Solution:

Interfaces in Java:

An interface in Java is a contract that defines what a class can do, without specifying how it does it. It's a reference type that contains only abstract methods and constants.

Key Features of Interfaces:

  • 100% Abstract: All methods are implicitly public and abstract (before Java 8)
  • Constants: All variables are implicitly public, static, and final
  • No Constructors: Interfaces cannot be instantiated directly
  • Multiple Inheritance: A class can implement multiple interfaces
  • Implementation Mandatory: Implementing class must provide implementation for all abstract methods

Multiple Inheritance Through Interfaces:

Java doesn't support multiple inheritance of classes to avoid ambiguity (Diamond Problem), but it supports multiple inheritance through interfaces because interfaces only define contracts, not implementation.

Practical Example - Smart Device System:

// Interface 1 - WiFi capability
interface WiFiEnabled {
    String WIFI_PROTOCOL = "802.11"; // Constant (implicitly public static final)
    
    void connectToWiFi(String networkName);
    void disconnectFromWiFi();
    boolean isWiFiConnected();
    String getWiFiStatus();
}

// Interface 2 - Bluetooth capability
interface BluetoothEnabled {
    double BLUETOOTH_VERSION = 5.0; // Constant
    
    void enableBluetooth();
    void disableBluetooth();
    void pairDevice(String deviceName);
    boolean isBluetoothOn();
}

// Interface 3 - Camera capability
interface CameraEnabled {
    int MAX_RESOLUTION = 4096; // Constant
    
    void takePhoto();
    void recordVideo();
    void setResolution(int width, int height);
    String[] getSupportedFormats();
}

// Interface 4 - GPS capability
interface GPSEnabled {
    void enableGPS();
    void disableGPS();
    String getCurrentLocation();
    void navigateTo(String destination);
}

// Class implementing multiple interfaces
class Smartphone implements WiFiEnabled, BluetoothEnabled, CameraEnabled, GPSEnabled {
    private String model;
    private boolean wifiConnected;
    private boolean bluetoothOn;
    private boolean gpsEnabled;
    private int cameraWidth = 1920;
    private int cameraHeight = 1080;
    
    public Smartphone(String model) {
        this.model = model;
        this.wifiConnected = false;
        this.bluetoothOn = false;
        this.gpsEnabled = false;
    }
    
    // Implement WiFiEnabled methods
    @Override
    public void connectToWiFi(String networkName) {
        wifiConnected = true;
        System.out.println(model + " connected to WiFi network: " + networkName);
        System.out.println("Using protocol: " + WIFI_PROTOCOL);
    }
    
    @Override
    public void disconnectFromWiFi() {
        wifiConnected = false;
        System.out.println(model + " disconnected from WiFi");
    }
    
    @Override
    public boolean isWiFiConnected() {
        return wifiConnected;
    }
    
    @Override
    public String getWiFiStatus() {
        return wifiConnected ? "WiFi Connected" : "WiFi Disconnected";
    }
    
    // Implement BluetoothEnabled methods
    @Override
    public void enableBluetooth() {
        bluetoothOn = true;
        System.out.println(model + " Bluetooth enabled (Version " + BLUETOOTH_VERSION + ")");
    }
    
    @Override
    public void disableBluetooth() {
        bluetoothOn = false;
        System.out.println(model + " Bluetooth disabled");
    }
    
    @Override
    public void pairDevice(String deviceName) {
        if (bluetoothOn) {
            System.out.println(model + " paired with " + deviceName);
        } else {
            System.out.println("Enable Bluetooth first!");
        }
    }
    
    @Override
    public boolean isBluetoothOn() {
        return bluetoothOn;
    }
    
    // Implement CameraEnabled methods
    @Override
    public void takePhoto() {
        System.out.println(model + " taking photo at " + cameraWidth + "x" + cameraHeight);
    }
    
    @Override
    public void recordVideo() {
        System.out.println(model + " recording video at " + cameraWidth + "x" + cameraHeight);
    }
    
    @Override
    public void setResolution(int width, int height) {
        if (width <= MAX_RESOLUTION && height <= MAX_RESOLUTION) {
            this.cameraWidth = width;
            this.cameraHeight = height;
            System.out.println("Camera resolution set to " + width + "x" + height);
        } else {
            System.out.println("Resolution exceeds maximum: " + MAX_RESOLUTION);
        }
    }
    
    @Override
    public String[] getSupportedFormats() {
        return new String[]{"JPEG", "PNG", "MP4", "AVI"};
    }
    
    // Implement GPSEnabled methods
    @Override
    public void enableGPS() {
        gpsEnabled = true;
        System.out.println(model + " GPS enabled");
    }
    
    @Override
    public void disableGPS() {
        gpsEnabled = false;
        System.out.println(model + " GPS disabled");
    }
    
    @Override
    public String getCurrentLocation() {
        if (gpsEnabled) {
            return "Latitude: 40.7128, Longitude: -74.0060"; // NYC coordinates
        } else {
            return "GPS disabled";
        }
    }
    
    @Override
    public void navigateTo(String destination) {
        if (gpsEnabled) {
            System.out.println(model + " navigating to: " + destination);
        } else {
            System.out.println("Enable GPS for navigation!");
        }
    }
    
    // Additional smartphone-specific method
    public void displayDeviceInfo() {
        System.out.println("=== Device Information ===");
        System.out.println("Model: " + model);
        System.out.println("WiFi Status: " + getWiFiStatus());
        System.out.println("Bluetooth: " + (bluetoothOn ? "On" : "Off"));
        System.out.println("GPS: " + (gpsEnabled ? "Enabled" : "Disabled"));
        System.out.println("Camera Resolution: " + cameraWidth + "x" + cameraHeight);
        System.out.println("==========================");
    }
}

// Demo class showing multiple inheritance through interfaces
public class MultipleInheritanceDemo {
    
    // Methods demonstrating polymorphism
    public static void testWiFiCapability(WiFiEnabled device) {
        device.connectToWiFi("MyHomeNetwork");
        System.out.println("WiFi Status: " + device.getWiFiStatus());
    }
    
    public static void testBluetoothCapability(BluetoothEnabled device) {
        device.enableBluetooth();
        device.pairDevice("Wireless Headphones");
    }
    
    public static void testCameraCapability(CameraEnabled device) {
        device.setResolution(1920, 1080);
        device.takePhoto();
        System.out.println("Supported formats: " + 
                          String.join(", ", device.getSupportedFormats()));
    }
    
    public static void testGPSCapability(GPSEnabled device) {
        device.enableGPS();
        System.out.println("Current location: " + device.getCurrentLocation());
        device.navigateTo("Times Square, New York");
    }
    
    public static void main(String[] args) {
        System.out.println("=== Multiple Inheritance Through Interfaces Demo ===\n");
        
        // Create smartphone object
        Smartphone iPhone = new Smartphone("iPhone 14 Pro");
        
        // Display initial device info
        iPhone.displayDeviceInfo();
        
        System.out.println("\n=== Testing Individual Capabilities ===");
        
        // Test each interface capability (polymorphic behavior)
        System.out.println("\n1. Testing WiFi Capability:");
        testWiFiCapability(iPhone); // iPhone treated as WiFiEnabled
        
        System.out.println("\n2. Testing Bluetooth Capability:");
        testBluetoothCapability(iPhone); // iPhone treated as BluetoothEnabled
        
        System.out.println("\n3. Testing Camera Capability:");
        testCameraCapability(iPhone); // iPhone treated as CameraEnabled
        
        System.out.println("\n4. Testing GPS Capability:");
        testGPSCapability(iPhone); // iPhone treated as GPSEnabled
        
        // Demonstrate interface array (polymorphic array)
        System.out.println("\n=== Polymorphic Array Example ===");
        
        WiFiEnabled[] wifiDevices = {iPhone}; // Can add more WiFi devices
        BluetoothEnabled[] bluetoothDevices = {iPhone}; // Same object, different interface
        
        System.out.println("WiFi devices in array: " + wifiDevices.length);
        System.out.println("Bluetooth devices in array: " + bluetoothDevices.length);
        
        // Updated device info
        System.out.println("\n=== Final Device State ===");
        iPhone.displayDeviceInfo();
        
        System.out.println("\n=== Key Benefits Demonstrated ===");
        System.out.println("1. Single class (Smartphone) implements multiple interfaces");
        System.out.println("2. Each interface provides specific contract/capability");
        System.out.println("3. Same object can be treated as different interface types");
        System.out.println("4. No ambiguity - each interface method has single implementation");
        System.out.println("5. Easy to extend with new interfaces without modifying existing code");
    }
}

How Multiple Inheritance is Achieved:

  1. Interface Definition: Multiple interfaces define different contracts
  2. Implementation: Single class implements multiple interfaces using 'implements' keyword
  3. Method Implementation: Class provides implementation for all abstract methods from all interfaces
  4. Polymorphism: Object can be treated as any of its implemented interface types
  5. No Ambiguity: Since interfaces don't have implementation (originally), there's no confusion about which method to call

Q2. (GTU Winter 2021) Compare interface and abstract class in Java. When would you choose one over the other? Provide examples.

Solution:

Comparison: Interface vs Abstract Class

AspectInterfaceAbstract Class
Keywordinterfaceabstract class
Multiple InheritanceSupported (class implements multiple)Not supported (class extends only one)
Method TypesAbstract, default (Java 8+), staticAbstract, concrete
Variable Typespublic static final only (constants)Any type (instance, static, final)
ConstructorsCannot have constructorsCan have constructors
Access ModifiersMethods implicitly publicAny access modifier allowed
InstantiationCannot be instantiatedCannot be instantiated
Implementation Level0% (originally), partial with Java 8+0% to 99% implementation

When to Choose Interface:

Use Interface When:

  • Multiple inheritance is needed
  • Defining a contract (what classes should do)
  • Loose coupling is important
  • No shared state among implementations
  • Plugin architecture or strategy pattern

Interface Example:

// Example: Media Player Interface
interface Playable {
    void play();
    void pause();
    void stop();
}

interface Downloadable {
    void download(String url);
    boolean isDownloaded();
}

// Class can implement multiple interfaces
class MusicPlayer implements Playable, Downloadable {
    private boolean isPlaying = false;
    private boolean downloaded = false;
    
    @Override
    public void play() {
        isPlaying = true;
        System.out.println("Music is playing");
    }
    
    @Override
    public void pause() {
        isPlaying = false;
        System.out.println("Music paused");
    }
    
    @Override
    public void stop() {
        isPlaying = false;
        System.out.println("Music stopped");
    }
    
    @Override
    public void download(String url) {
        downloaded = true;
        System.out.println("Downloading from: " + url);
    }
    
    @Override
    public boolean isDownloaded() {
        return downloaded;
    }
}

When to Choose Abstract Class:

Use Abstract Class When:

  • Shared implementation among related classes
  • Common state needs to be maintained
  • Constructor is needed
  • Code reusability is priority
  • Template method pattern

Abstract Class Example:

// Example: Vehicle Abstract Class
abstract class Vehicle {
    // Instance variables (state)
    protected String brand;
    protected String model;
    protected int year;
    protected double fuelLevel;
    
    // Constructor
    public Vehicle(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.fuelLevel = 100.0; // Full tank
    }
    
    // Concrete methods (shared implementation)
    public void startEngine() {
        if (fuelLevel > 0) {
            System.out.println(brand + " " + model + " engine started");
        } else {
            System.out.println("Cannot start - no fuel!");
        }
    }
    
    public void stopEngine() {
        System.out.println(brand + " " + model + " engine stopped");
    }
    
    public void refuel(double amount) {
        fuelLevel = Math.min(100.0, fuelLevel + amount);
        System.out.println("Refueled. Current fuel level: " + fuelLevel + "%");
    }
    
    // Template method - defines algorithm structure
    public final void performMaintenance() {
        System.out.println("Starting maintenance for " + brand + " " + model);
        checkEngine();      // Abstract - subclass implements
        changeFluids();     // Concrete - common for all
        checkSpecificParts(); // Abstract - subclass implements
        System.out.println("Maintenance completed");
    }
    
    // Common concrete method
    private void changeFluids() {
        System.out.println("Changing engine oil and other fluids");
    }
    
    // Abstract methods - must be implemented by subclasses
    public abstract void accelerate();
    public abstract void brake();
    public abstract double getFuelEfficiency();
    protected abstract void checkEngine();
    protected abstract void checkSpecificParts();
    
    // Getters
    public String getBrand() { return brand; }
    public String getModel() { return model; }
    public double getFuelLevel() { return fuelLevel; }
}

// Concrete implementation
class Car extends Vehicle {
    private int numberOfDoors;
    
    public Car(String brand, String model, int year, int doors) {
        super(brand, model, year); // Call parent constructor
        this.numberOfDoors = doors;
    }
    
    @Override
    public void accelerate() {
        if (fuelLevel > 0) {
            fuelLevel -= 0.5;
            System.out.println("Car accelerating smoothly");
        }
    }
    
    @Override
    public void brake() {
        System.out.println("Car braking with ABS");
    }
    
    @Override
    public double getFuelEfficiency() {
        return 15.0; // km per liter
    }
    
    @Override
    protected void checkEngine() {
        System.out.println("Checking car engine and exhaust system");
    }
    
    @Override
    protected void checkSpecificParts() {
        System.out.println("Checking doors (" + numberOfDoors + "), windows, and air conditioning");
    }
    
    // Car-specific method
    public void openTrunk() {
        System.out.println("Car trunk opened");
    }
}

class Motorcycle extends Vehicle {
    private String engineType;
    
    public Motorcycle(String brand, String model, int year, String engineType) {
        super(brand, model, year);
        this.engineType = engineType;
    }
    
    @Override
    public void accelerate() {
        if (fuelLevel > 0) {
            fuelLevel -= 0.8;
            System.out.println("Motorcycle accelerating quickly");
        }
    }
    
    @Override
    public void brake() {
        System.out.println("Motorcycle braking carefully");
    }
    
    @Override
    public double getFuelEfficiency() {
        return 30.0; // km per liter
    }
    
    @Override
    protected void checkEngine() {
        System.out.println("Checking " + engineType + " motorcycle engine");
    }
    
    @Override
    protected void checkSpecificParts() {
        System.out.println("Checking chain, sprockets, and helmet storage");
    }
}

Demonstration Program:

public class InterfaceVsAbstractDemo {
    public static void main(String[] args) {
        System.out.println("=== Interface Example ===");
        
        // Interface allows multiple inheritance
        MusicPlayer player = new MusicPlayer();
        
        // Can be treated as either interface
        Playable playableDevice = player;
        Downloadable downloadableDevice = player;
        
        playableDevice.play();
        downloadableDevice.download("http://music.com/song.mp3");
        
        System.out.println("\n=== Abstract Class Example ===");
        
        // Abstract class provides shared implementation
        Car car = new Car("Toyota", "Camry", 2023, 4);
        Motorcycle bike = new Motorcycle("Honda", "CBR", 2023, "4-Stroke");
        
        // Common methods inherited
        car.startEngine();
        car.refuel(20);
        car.accelerate();
        
        bike.startEngine();
        bike.accelerate();
        
        // Template method pattern
        System.out.println("\n=== Template Method Pattern ===");
        car.performMaintenance();   // Follows same algorithm
        bike.performMaintenance();  // But different implementations
        
        System.out.println("\n=== Decision Guidelines ===");
        System.out.println("Choose Interface when:");
        System.out.println("- Multiple inheritance needed");
        System.out.println("- Defining contracts/capabilities");
        System.out.println("- Loose coupling important");
        
        System.out.println("\nChoose Abstract Class when:");
        System.out.println("- Shared state and behavior needed");
        System.out.println("- Constructor required");
        System.out.println("- Template method pattern");
    }
}

Decision Matrix:

Choose Interface if:

  • You need multiple inheritance
  • You're defining what classes CAN DO (capabilities)
  • Implementations are likely to be very different
  • You want loose coupling

Choose Abstract Class if:

  • You have common code to share
  • You need to maintain state
  • You need constructors
  • You're defining what classes ARE (identity)

Q3. (GTU Summer 2020) Explain default methods in Java 8 interfaces. How do they solve interface evolution problems? Provide examples.

Solution:

Default Methods in Java 8 Interfaces:

Default methods are methods in interfaces that have a default implementation. They were introduced in Java 8 to allow interfaces to evolve without breaking existing implementations.

Interface Evolution Problem:

Before Java 8, adding a new method to an interface would break all existing classes that implement that interface, because they would need to implement the new method.

How Default Methods Solve the Problem:

  • Allow adding new methods to interfaces without breaking compatibility
  • Provide default implementation that existing classes can inherit
  • Implementing classes can choose to override the default implementation
  • Enable interface evolution while maintaining backward compatibility

Example: Library Management Interface Evolution

// Original interface (before enhancement)
interface Library {
    void addBook(String title, String author);
    void removeBook(String isbn);
    boolean findBook(String isbn);
}

// Original implementation
class CityLibrary implements Library {
    private Map books = new HashMap<>();
    
    @Override
    public void addBook(String title, String author) {
        String isbn = generateISBN();
        books.put(isbn, title + " by " + author);
        System.out.println("Added: " + title + " by " + author);
    }
    
    @Override
    public void removeBook(String isbn) {
        books.remove(isbn);
        System.out.println("Removed book with ISBN: " + isbn);
    }
    
    @Override
    public boolean findBook(String isbn) {
        return books.containsKey(isbn);
    }
    
    private String generateISBN() {
        return "ISBN-" + System.currentTimeMillis() % 10000;
    }
}

// Enhanced interface with default methods (Java 8+)
interface EnhancedLibrary {
    // Original abstract methods
    void addBook(String title, String author);
    void removeBook(String isbn);
    boolean findBook(String isbn);
    
    // New default methods - don't break existing implementations
    default void displayLibraryInfo() {
        System.out.println("=== Library Information ===");
        System.out.println("Library System Version: 2.0");
        System.out.println("Supports: Books, eBooks, Audiobooks");
        System.out.println("============================");
    }
    
    default void sendOverdueNotice(String memberEmail) {
        System.out.println("Sending overdue notice to: " + memberEmail);
        System.out.println("Please return your books on time.");
        logActivity("Overdue notice sent to " + memberEmail);
    }
    
    default void generateReport() {
        System.out.println("Generating standard library report...");
        System.out.println("Total books: " + getTotalBooks());
        System.out.println("System status: Active");
    }
    
    // Default method calling abstract method
    default int getTotalBooks() {
        // This would need to be overridden in implementation
        return 0; // Default implementation
    }
    
    // Default method with parameters and logic
    default boolean isValidISBN(String isbn) {
        return isbn != null && isbn.startsWith("ISBN-") && isbn.length() >= 9;
    }
    
    // Private method (Java 9+) to support default methods
    private void logActivity(String activity) {
        System.out.println("[LOG] " + java.time.LocalDateTime.now() + ": " + activity);
    }
    
    // Static method in interface
    static String getLibraryStandard() {
        return "ISO 2108 - International Standard Book Number";
    }
}

// Updated implementation - no need to change existing methods
class ModernLibrary implements EnhancedLibrary {
    private Map books = new HashMap<>();
    private int totalBooks = 0;
    
    @Override
    public void addBook(String title, String author) {
        String isbn = generateISBN();
        if (isValidISBN(isbn)) { // Using default method
            books.put(isbn, title + " by " + author);
            totalBooks++;
            System.out.println("Added: " + title + " by " + author + " (ISBN: " + isbn + ")");
        } else {
            System.out.println("Invalid ISBN generated!");
        }
    }
    
    @Override
    public void removeBook(String isbn) {
        if (books.remove(isbn) != null) {
            totalBooks--;
            System.out.println("Removed book with ISBN: " + isbn);
        } else {
            System.out.println("Book not found: " + isbn);
        }
    }
    
    @Override
    public boolean findBook(String isbn) {
        return books.containsKey(isbn);
    }
    
    // Override default method to provide specific implementation
    @Override
    public int getTotalBooks() {
        return totalBooks;
    }
    
    // Can choose to override other default methods
    @Override
    public void generateReport() {
        System.out.println("=== Modern Library Report ===");
        System.out.println("Library Name: Modern City Library");
        System.out.println("Total Books: " + getTotalBooks());
        System.out.println("Standard: " + EnhancedLibrary.getLibraryStandard());
        System.out.println("Digital Catalog: Enabled");
        System.out.println("Online Access: 24/7");
        System.out.println("==============================");
    }
    
    // Custom method to override default behavior
    @Override
    public void sendOverdueNotice(String memberEmail) {
        System.out.println("Sending personalized overdue notice to: " + memberEmail);
        System.out.println("Dear Member,");
        System.out.println("You have overdue books. Please return them to avoid fines.");
        System.out.println("You can also renew online at our website.");
        System.out.println("Thank you - Modern City Library");
        // Note: Can't call private logActivity directly, but default method handles it
    }
    
    private String generateISBN() {
        return "ISBN-" + (1000000000L + System.currentTimeMillis() % 9000000000L);
    }
}

// Legacy implementation - still works without modification
class LegacyLibrary implements EnhancedLibrary {
    private List bookList = new ArrayList<>();
    
    @Override
    public void addBook(String title, String author) {
        bookList.add(title + " by " + author);
        System.out.println("Legacy: Added " + title);
    }
    
    @Override
    public void removeBook(String isbn) {
        // Legacy implementation - remove by title search
        bookList.removeIf(book -> book.contains(isbn));
        System.out.println("Legacy: Attempted to remove book");
    }
    
    @Override
    public boolean findBook(String isbn) {
        return bookList.stream().anyMatch(book -> book.contains(isbn));
    }
    
    // Uses default implementation of other methods
    // No need to implement displayLibraryInfo, sendOverdueNotice, etc.
}

Demonstration Program:

public class DefaultMethodDemo {
    public static void main(String[] args) {
        System.out.println("=== Default Methods Evolution Demo ===\n");
        
        // Modern implementation
        ModernLibrary modernLib = new ModernLibrary();
        
        // Legacy implementation - still works!
        LegacyLibrary legacyLib = new LegacyLibrary();
        
        // Test both implementations
        System.out.println("=== Modern Library ===");
        modernLib.addBook("Java Programming", "John Doe");
        modernLib.addBook("Data Structures", "Jane Smith");
        
        // Use default methods
        modernLib.displayLibraryInfo();
        modernLib.generateReport(); // Overridden implementation
        modernLib.sendOverdueNotice("user@example.com"); // Overridden
        
        System.out.println("\n=== Legacy Library ===");
        legacyLib.addBook("Old Programming Book", "Legacy Author");
        
        // Legacy library gets default implementations automatically!
        legacyLib.displayLibraryInfo(); // Uses default implementation
        legacyLib.generateReport();     // Uses default implementation
        legacyLib.sendOverdueNotice("legacy@example.com"); // Uses default
        
        // Static method usage
        System.out.println("\n=== Interface Static Method ===");
        System.out.println("Library Standard: " + EnhancedLibrary.getLibraryStandard());
        
        // Demonstrate polymorphism with default methods
        System.out.println("\n=== Polymorphic Default Method Usage ===");
        
        EnhancedLibrary[] libraries = {modernLib, legacyLib};
        
        for (EnhancedLibrary lib : libraries) {
            System.out.println("\nTesting library:");
            lib.displayLibraryInfo(); // Same interface, different or same implementation
        }
        
        System.out.println("\n=== Benefits Demonstrated ===");
        System.out.println("1. Backward Compatibility: Legacy code still works");
        System.out.println("2. Interface Evolution: New methods added without breaking changes");
        System.out.println("3. Optional Override: Modern implementations can customize behavior");
        System.out.println("4. Default Behavior: Sensible defaults provided");
        System.out.println("5. Code Reuse: Common logic in default methods");
    }
}

Key Benefits of Default Methods:

  1. Backward Compatibility: Existing implementations don't break when interface evolves
  2. Interface Evolution: Can add new functionality to existing interfaces
  3. Optional Implementation: Implementing classes can choose to override or use default
  4. Code Reuse: Common logic can be shared across implementations
  5. Library Enhancement: Enables frameworks like Java Collections to add new methods

Rules for Default Methods:

  • Must be declared with default keyword
  • Must have implementation body
  • Cannot be abstract, final, static, or synchronized
  • Can be overridden in implementing classes
  • Can call other default methods and abstract methods
  • If class implements multiple interfaces with same default method, must override to resolve conflict

Real-world Impact:

Default methods enabled Java 8 to add lambda expression support to existing Collection interfaces (like forEach, stream) without breaking millions of existing implementations.

Lecture Summary

Key Concepts Covered:

  • Interface fundamentals and syntax
  • Multiple inheritance through interfaces
  • Interface inheritance and extension
  • Default and static methods (Java 8+)
  • Functional interfaces and lambda expressions
  • Interface vs abstract class comparison
  • Real-world interface design patterns

Learning Outcomes Achieved:

  • ✅ Master interface concepts and implementation
  • ✅ Apply multiple inheritance effectively
  • ✅ Use default methods for interface evolution
  • ✅ Design flexible and maintainable systems
  • ✅ Choose between interfaces and abstract classes
  • ✅ Implement plugin and strategy patterns
  • ✅ Create testable and loosely coupled code

Next Lecture: Exception Handling Fundamentals

Topics: Exception hierarchy, try-catch blocks, finally clause, custom exceptions

Thank You!

Questions & Discussion


Next: Lecture 13 - Exception Handling Fundamentals


Course: 4343203 Java Programming
Unit 3: Exception Handling and Advanced Topics
GTU Semester 4