Java Question Bank with Answers

 

Answers for Core Java Question Bank –

Chapter 3: Inheritance and Interfaces

Difficult Level Questions

18. Design a Java program using inheritance to model a "University Employee" system.

·         Create an abstract class Employee with common attributes (e.g., name, employeeId) and an abstract method calculateSalary().

·         Create two concrete subclasses: Faculty (with additional attributes like department, designation) and Staff (with additional attributes like jobTitle).

·         Implement calculateSalary() uniquely for Faculty (e.g., based on designation) and Staff (e.g., based on fixed monthly salary).

·         Demonstrate runtime polymorphism by creating an array of Employee objects, populating it with Faculty and Staff instances, and calling calculateSalary() on each.

Answer:

import java.util.ArrayList;
import java.util.List;
 
// Abstract Superclass: Employee
abstract class Employee {
    private String name;
    private String employeeId;
 
    // Parameterized Constructor
    public Employee(String name, String employeeId) {
        this.name = name;
        this.employeeId = employeeId;
    }
 
    // Abstract method: calculateSalary
    public abstract double calculateSalary();
 
    // Concrete method: display basic employee info
    public void displayEmployeeInfo() {
        System.out.println("Name: " + name + ", ID: " + employeeId);
    }
 
    // Getters
    public String getName() { return name; }
    public String getEmployeeId() { return employeeId; }
}
 
// Concrete Subclass 1: Faculty
class Faculty extends Employee {
    private String department;
    private String designation;
 
    public Faculty(String name, String employeeId, String department, String designation) {
        super(name, employeeId);
        this.department = department;
        this.designation = designation;
    }
 
    @Override
    public double calculateSalary() {
        double baseSalary = 50000.0;
        switch (designation.toLowerCase()) {
            case "professor":
                return baseSalary + 20000.0;
            case "associate professor":
                return baseSalary + 10000.0;
            case "assistant professor":
                return baseSalary + 5000.0;
            default:
                return baseSalary;
        }
    }
 
    @Override
    public void displayEmployeeInfo() {
        super.displayEmployeeInfo();
        System.out.println("  Department: " + department + ", Designation: " + designation);
    }
}
 
// Concrete Subclass 2: Staff
class Staff extends Employee {
    private String jobTitle;
    private double monthlyRate;
 
    public Staff(String name, String employeeId, String jobTitle, double monthlyRate) {
        super(name, employeeId);
        this.jobTitle = jobTitle;
        this.monthlyRate = monthlyRate;
    }
 
    @Override
    public double calculateSalary() {
        return monthlyRate;
    }
 
    @Override
    public void displayEmployeeInfo() {
        super.displayEmployeeInfo();
        System.out.println("  Job Title: " + jobTitle);
    }
}
 
// Main class
public class UniversityEmployeeSystem {
    public static void main(String[] args) {
        List<Employee> universityEmployees = new ArrayList<>();
 
        // Adding Faculty
        universityEmployees.add(new Faculty("Dr. Alice Smith", "F001", "Computer Science", "Professor"));
        universityEmployees.add(new Faculty("Dr. Bob Johnson", "F002", "Physics", "Associate Professor"));
        universityEmployees.add(new Faculty("Dr. Carol White", "F003", "Mathematics", "Assistant Professor"));
 
        // Adding Staff
        universityEmployees.add(new Staff("David Brown", "S001", "Administrative Assistant", 35000.0));
        universityEmployees.add(new Staff("Eve Green", "S002", "Lab Technician", 40000.0));
        universityEmployees.add(new Staff("Frank Black", "S003", "Security Guard", 30000.0));
 
        System.out.println("--- University Employee Payroll ---");
        for (Employee emp : universityEmployees) {
            emp.displayEmployeeInfo();
            System.out.printf("  Calculated Salary: $%.2f%n", emp.calculateSalary());
            System.out.println("----------------------------------");
        }
    }
}

19. Evaluate the benefits and drawbacks of using multiple interfaces versus a single abstract class for achieving polymorphism in complex Java applications. Under what specific conditions would multiple interfaces be a superior design choice, and when would an abstract class be more appropriate? Provide a detailed justification with examples.

Answer:
Both multiple interfaces and abstract classes are powerful mechanisms for achieving polymorphism and abstraction in Java. The choice between them depends on the specific design requirements, the nature of the "is-a" versus "can-do" relationships, and the flexibility needed in the system.

Multiple Interfaces:

·         Benefits:

1.     Supports Multiple Behavioral Contracts: A class can implement any number of interfaces, allowing it to conform to multiple distinct behavioral contracts without the limitations of single inheritance . This is crucial for designing highly flexible and extensible systems.

2.     Achieves Multiple Inheritance of Type: Effectively allows a form of multiple inheritance, where a class can inherit behaviors (method signatures) from several sources.

3.     Loose Coupling: Interfaces enforce a contract without dictating implementation details (before Java 8 default methods), promoting loose coupling between the interface and its implementations.

4.     Flexibility in Class Hierarchy: Interfaces can be implemented by classes anywhere in the inheritance hierarchy, regardless of their superclass.

5.     API Definition: Excellent for defining public APIs and frameworks where implementers need freedom to structure their concrete classes.

·         Drawbacks:

1.     No Shared Implementation: Historically, interfaces could not provide any shared code, leading to code duplication if multiple implementing classes needed identical default behavior. (Mitigated by Java 8 default methods, but they come with their own considerations like potential ambiguity if conflicting default methods are inherited.)

2.     All Fields are static final: Cannot define non-constant fields (instance variables), which means interfaces cannot hold state.

3.     No Constructors: Cannot define constructors, making common initialization logic impossible.

Single Abstract Class:

·         Benefits:

1.     Shared Implementation: Can provide a partial implementation (concrete methods) that can be inherited and reused by all subclasses . This reduces code duplication for common behaviors.

2.     Shared State: Can define and hold instance variables (state) that are common to all its subclasses.

3.     Constructors: Can have constructors, allowing for common initialization logic for the abstract class’s fields.

4.     "Is-A" Relationship: Best suited for defining a strong "is-a" relationship within a tightly coupled hierarchy.

·         Drawbacks:

1.     Single Inheritance Limit: A class can extend only one abstract class, limiting its ability to inherit common implementations or states from multiple parents.

2.     Tighter Coupling: Due to shared implementation and state, subclasses are more tightly coupled to the abstract class’s design.

3.     Less Flexible: Can constrain the design if a class needs to inherit behavior from multiple, disparate sources that don’t fit into a single "is-a" hierarchy.

When Multiple Interfaces are Superior:

·         Conditions:

1.     Behavioral Contracts for Unrelated Classes: When defining common behaviors that can be implemented by classes from entirely different inheritance hierarchies. For example, Flyable might be implemented by Bird, Airplane, and Superman.

2.     Adding Capabilities to Existing Classes: If a class already extends another class, it can still implement new interfaces to gain additional capabilities without changing its superclass.

3.     API Design for Extensibility: When designing a flexible API or framework where you want to specify "what" services are available without dictating "how" they are implemented, allowing maximum freedom to implementers.

4.     Mimicking Multiple Inheritance of Behavior: When a class needs to exhibit characteristics of several distinct types (e.g., an AmphibiousVehicle might implement Driveable and Swimmable).

·         Example: A system where different objects can be Savable, Printable, and Searchable. Instead of a complex abstract class hierarchy, interfaces ISavable, IPrintable, and ISearchable can be implemented by various classes (Document, Image, Report) as needed.

When an Abstract Class is More Appropriate:

·         Conditions:

1.     Strong "Is-A" Relationship with Common Implementation: When there’s a clear hierarchical relationship where subclasses share significant common state and behavior, and you want to provide a base implementation that subclasses can inherit or override.

2.     Code Reusability for Default Behavior: When you want to provide common methods and fields that most subclasses will use, but also allow for specialized behaviors via abstract methods.

3.     Version Control of API: When adding new methods to an abstract class, existing concrete subclasses might need to be updated, which is a stronger coupling than interfaces typically offer (though Java 8 default methods have blurred this line for interfaces).

4.     Classes with Common State and Constructor Logic: When the base type needs common instance fields that are initialized through a constructor shared by all subclasses.

·         Example: A hierarchy for BankAccount. An abstract BankAccount could have accountNumber, balance, deposit() and withdraw() (concrete methods), and an abstract calculateInterest() method. SavingsAccount and CheckingAccount would extend BankAccount, inheriting the common features and implementing calculateInterest() uniquely.