Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Java Inheritance in Action: Building a Book Management System with Abstract Classes

Learn Java inheritance by building a book management system with abstract classes, constructors, and toString overriding. Perfect for COP3330 students.

Java inheritance tutorial abstract class Java example COP3330 homework 3 book management system Java Java polymorphism example Java toString override Java constructor chaining Java instanceof operator Java inheritance assignment Java OOP project Java bookstore library system Java call number generation Java abstract class vs interface Java inheritance best practices Java programming assignment help Java object-oriented programming

Introduction: Why Inheritance Matters in Java

Inheritance is one of the four pillars of object-oriented programming (OOP). It allows you to create a hierarchy of classes where child classes reuse and extend the functionality of a parent class. This tutorial walks you through a practical assignment from COP3330: building two types of books—BookstoreBook and LibraryBook—that share common fields via an abstract Book class. You'll learn how to use constructors, getters, setters, and the toString method effectively.

Understanding the Assignment

The goal is to write code for two classes: BookstoreBook and LibraryBook. Both have fields: author, title, and isbn. The BookstoreBook adds a price and sale status (with discount percentage). The LibraryBook adds a call number generated automatically. You need to create an abstract class Book with shared fields and methods, then extend it. A BookList class holds an array of 100 Book objects. The Main class interacts with the user.

Step 1: Create the Abstract Book Class

The abstract class Book contains the common fields: author, title, isbn. It also defines abstract methods if needed. Here's the skeleton:

abstract class Book {
    private String author;
    private String title;
    private String isbn;

    // Constructors
    public Book() { }
    public Book(String author, String title, String isbn) {
        this.author = author;
        this.title = title;
        this.isbn = isbn;
    }

    // Getters and setters
    public String getAuthor() { return author; }
    public void setAuthor(String author) { this.author = author; }
    // ... similar for title and isbn

    // Abstract method? Not required here, but you can add a toString override
    @Override
    public String toString() {
        return isbn + "-" + title.toUpperCase() + " by " + author.toUpperCase();
    }
}

Notice that toString is overridden to provide a formatted string. This is a common practice for displaying object information.

Step 2: Extend Book with BookstoreBook

The BookstoreBook class adds price (double), onSale (boolean), and discountPercent (double). It must have at least three constructors. Override toString to show the listed price and sale price.

class BookstoreBook extends Book {
    private double price;
    private boolean onSale;
    private double discountPercent;

    // Constructors
    public BookstoreBook() { super(); }
    public BookstoreBook(String author, String title, String isbn, double price) {
        super(author, title, isbn);
        this.price = price;
        this.onSale = false;
        this.discountPercent = 0;
    }
    public BookstoreBook(String author, String title, String isbn, double price, boolean onSale, double discountPercent) {
        super(author, title, isbn);
        this.price = price;
        this.onSale = onSale;
        this.discountPercent = discountPercent;
    }

    // Getters and setters
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
    // ... other getters/setters

    @Override
    public String toString() {
        double salePrice = onSale ? price * (1 - discountPercent / 100) : price;
        return super.toString() + ", $" + String.format("%.2f", price) + " listed for $" + String.format("%.2f", salePrice);
    }
}

In the toString, we call super.toString() to reuse the base formatting, then append price info. This is a classic example of inheritance and method overriding.

Step 3: Extend Book with LibraryBook

The LibraryBook class has a callNumber string that is auto-generated from the author's first initial and last name, plus the last digit of the ISBN. For example, author "Eric Jones" and ISBN "958792130" yields "09.ERI.0".

class LibraryBook extends Book {
    private String callNumber;

    // Constructors
    public LibraryBook() { super(); }
    public LibraryBook(String author, String title, String isbn) {
        super(author, title, isbn);
        this.callNumber = generateCallNumber(author, isbn);
    }

    // Generate call number
    private String generateCallNumber(String author, String isbn) {
        String[] parts = author.toUpperCase().split(" ");
        String first = parts[0].substring(0, 1);
        String last = parts[1].substring(0, 3);
        String lastDigit = isbn.substring(isbn.length() - 1);
        return lastDigit + "." + last + "." + first;
    }

    // Getter for call number (no setter if auto-generated)
    public String getCallNumber() { return callNumber; }

    @Override
    public String toString() {
        return super.toString() + "-" + callNumber;
    }
}

Note that the call number generation follows a specific pattern. This demonstrates how subclass-specific logic can be encapsulated.

Step 4: Build the BookList Class

The BookList class holds an array of 100 Book references. It manages adding books and displaying them. Since Book is abstract, you cannot instantiate it, but you can store subclass objects in the array.

class BookList {
    private Book[] list;
    private int count;

    public BookList() {
        list = new Book[100];
        count = 0;
    }

    public void addBook(Book b) {
        if (count < 100) {
            list[count] = b;
            count++;
        }
    }

    public void displayBooks() {
        int libCount = 0, storeCount = 0;
        for (int i = 0; i < count; i++) {
            if (list[i] instanceof LibraryBook) libCount++;
            else if (list[i] instanceof BookstoreBook) storeCount++;
        }
        System.out.println("Library Books (" + libCount + ")");
        for (int i = 0; i < count; i++) {
            if (list[i] instanceof LibraryBook) {
                System.out.println("[" + list[i].toString() + "]");
            }
        }
        System.out.println("_ _ _ _");
        System.out.println("Bookstore Books (" + storeCount + ")");
        for (int i = 0; i < count; i++) {
            if (list[i] instanceof BookstoreBook) {
                System.out.println("[" + list[i].toString() + "]");
            }
        }
        System.out.println("_ _ _ _");
    }
}

instanceof is used to differentiate between the two types. This is a common pattern when working with polymorphic arrays.

Step 5: Write the Main Class

The main class handles user interaction. It asks for book details, validates input, and creates objects. The sample run shows how the user enters data. Here's a simplified version:

public class Main {
    public static void main(String[] args) {
        BookList bookList = new BookList();
        Scanner scanner = new Scanner(System.in);
        System.out.println("Welcome to the book program!");
        boolean continueCreating = true;
        while (continueCreating) {
            System.out.println("Would you like to create a book object? (yes/no): ");
            String answer = scanner.nextLine().trim().toLowerCase();
            if (answer.equals("no")) {
                continueCreating = false;
            } else if (answer.equals("yes")) {
                // Get author, title, isbn
                System.out.println("Please enter the author, title and the isbn of the book separated by /: ");
                String[] parts = scanner.nextLine().split("/");
                String author = parts[0].trim();
                String title = parts[1].trim();
                String isbn = parts[2].trim();
                // Ask for type
                String type = "";
                while (true) {
                    System.out.println("Got it! Now, tell me if it is a bookstore book or a library book (enter BB for bookstore book or LB for library book): ");
                    type = scanner.nextLine().trim().toUpperCase();
                    if (type.equals("BB") || type.equals("LB")) break;
                    System.out.println("Oops! That's not a valid entry. Please try again: ");
                }
                if (type.equals("BB")) {
                    System.out.println("Got it! Please enter the list price of " + title.toUpperCase() + " by " + author.toUpperCase() + ": ");
                    double price = Double.parseDouble(scanner.nextLine());
                    System.out.println("Is it on sale? (y/n): ");
                    boolean onSale = scanner.nextLine().trim().equalsIgnoreCase("y");
                    double discount = 0;
                    if (onSale) {
                        System.out.println("Deduction percentage: ");
                        discount = Double.parseDouble(scanner.nextLine().replace("%", ""));
                    }
                    BookstoreBook bb = new BookstoreBook(author, title, isbn, price, onSale, discount);
                    bookList.addBook(bb);
                    System.out.println("Got it!");
                    System.out.println("Here is your bookstore book information [" + bb.toString() + "]");
                } else { // LB
                    LibraryBook lb = new LibraryBook(author, title, isbn);
                    bookList.addBook(lb);
                    System.out.println("Got it!");
                    System.out.println("Here is your library book information [" + lb.toString() + "]");
                }
            } else {
                System.out.println("I'm sorry but " + answer + " isn't a valid answer. Please enter either yes or no: ");
            }
        }
        System.out.println("Sure!");
        System.out.println("Here are all your books…");
        bookList.displayBooks();
        System.out.println("Take care now!");
        scanner.close();
    }
}

Polymorphism and the Power of Abstract Classes

By using an abstract Book class, you can treat all books uniformly. The array list holds Book references, but the actual objects are either BookstoreBook or LibraryBook. When you call toString, the correct overridden version is invoked—this is polymorphism.

Real-World Analogy: Library and Bookstore Inventory

Think of a library system that manages both physical books and digital books. Both have common attributes (title, author) but different specifics (shelf location vs. file size). Using inheritance, you can write code that works for both without duplication. Similarly, in a bookstore app, you might have PhysicalBook and EBook classes extending a common Book class.

Common Pitfalls and Tips

  • Constructor chaining: Always call super() in subclass constructors to initialize inherited fields.
  • ToString consistency: Use super.toString() in subclass to avoid repeating code.
  • Input validation: The sample run shows robust validation for yes/no and book type. Always handle invalid input gracefully.
  • Array size: The assignment specifies an array of 100. Ensure you don't exceed this limit.

Testing Your Code

Test with multiple books, both types, and edge cases like empty author names or missing ISBN. Ensure the call number generation works correctly. Compare your output to the sample run.

Conclusion

This assignment reinforces key OOP concepts: inheritance, abstract classes, constructors, method overriding, and polymorphism. By building a book management system, you gain practical experience that applies to real-world software development. Whether you're managing a library or an online bookstore, these patterns are foundational.

Remember: Java inheritance is like a family tree—each child class inherits traits from its parent but can add its own unique features. Master this, and you'll be ready for more advanced topics like interfaces and design patterns.