Java Inheritance, Interfaces and Polymorphism

Learn how Java classes build on each other. Extend a class with extends and super, override inherited methods, and give your classes useful toString, equals and hashCode methods from Object. Define shared behavior with abstract classes and interfaces, predict which method runs through polymorphism and dynamic binding, and model fixed options and simple data with enums and records. Worked code examples show each step, along with the mistakes beginners make most often.

BeginnerLesson 8 of 11 in Object-Oriented Programming with Java Course

What you will learn

  1. Build a new class on top of an existing one with extends, know what the new class inherits, and make sure the inherited part of every object is set up correctly with super.
    • Create a subclass with extends
      Declare a subclass, work out which members it inherits and can use, and decide with the is-a test whether inheritance fits.
    • Set up objects with superclass constructors
      Pass values to the superclass constructor with super(...), predict the order in which constructors run, and avoid the classic constructor mistakes.
  2. Change inherited behavior by overriding methods, build on the superclass version with super, and give your classes meaningful toString, equals and hashCode methods from Object.
    • Override inherited methods
      Replace or extend an inherited method with a correct override, call the superclass version with super, and tell overriding apart from overloading.
    • Use and override the methods of Object
      Explain why every object has toString, equals, hashCode and getClass, read their default behavior, and write a useful toString.
    • Override equals and hashCode correctly
      Define when two objects are equal with a correct equals(Object), keep hashCode consistent with it, and handle equality across a class hierarchy.
  3. Use abstract classes to share code among closely related classes, use interfaces to promise behavior across unrelated classes, and choose the right tool for a design.
    • Write abstract classes and abstract methods
      Declare abstract classes and methods, complete them in concrete subclasses, and use an abstract class to share fields and code.
    • Define and implement interfaces
      Write an interface as a contract, implement it in one or more classes, and combine interfaces with inheritance.
    • Add default, static and private methods to interfaces
      Give interfaces shared behavior with default methods, add helper methods with static and private methods, and resolve conflicts between default methods.
    • Choose between an abstract class and an interface
      Compare abstract classes and interfaces and pick the right one, or both together, for a given design.
  4. Refer to objects through general types, predict which method runs at run time, convert types safely, and write code that keeps working when new subclasses are added.
    • Refer to objects through superclass and interface types
      Distinguish a variable's declared type from the object's actual type, and use superclass and interface types for variables, parameters and lists.
    • Predict which method runs with dynamic binding
      Trace method calls through a class hierarchy, separating what the compiler decides from what is decided at run time.
    • Convert between types with casts and instanceof
      Downcast safely with instanceof and pattern matching, recognize ClassCastException, and know when a type test signals a missing overridden method.
    • Design flexible code with polymorphism
      Write methods and collections against general types so that new classes fit in without changes, and make classes sortable with Comparable.
  5. Represent a fixed set of options with an enum and a simple piece of data with a record, using the members the compiler generates for you.
    • Represent fixed sets of values with enums
      Declare enums, use their built-in methods and switch, and give enum constants fields, constructors and behavior.
    • Model data with records
      Declare records for simple data, use the generated constructor, accessors, equals, hashCode and toString, and validate data in a compact constructor.

Java Inheritance and Polymorphism study guide

5 chapters

1

Extend classes with inheritance

A subclass starts with the fields and behavior of an existing class, then adds what makes it different. That relationship affects both how other code can use the object and how Java builds it. Follow a person and a student through their declarations and constructors: private data stays under the superclass's control, and constructor calls supply the values that inherited state needs.

1.1 Build a subclass and protect its data

Inheritance creates a subclass from an existing superclass. In class Student extends Person, every student is a person. A student object holds its person state and its extra student state together in one object.

Add the student-specific parts

Here is a small superclass and a subclass:

class Person {
    private String name;
    public Person(String name) { this.name = name; }
    public String getName() { return name; }
}

class Student extends Person {
    private int credits;
    public Student(String name) { super(name); }
    public void study() { credits += 5; }
    public int getCredits() { return credits; }
}

Student inherits getName(). It adds a credits field and two methods. After Student s = new Student("Ada Lovelace");, the credits field starts at 0. Two calls to s.study() raise it first to 5 and then to 10. s.getName() still returns "Ada Lovelace": the inherited method reads the private field declared in Person.

The statement super(name) passes the name to the Person constructor. Constructors are not inherited, so Student declares its own constructor. A subclass may add methods and override inherited behavior, but it cannot remove an inherited public method from its public API.

Know which members are accessible
Access modifierWhere the member can be accessed
publicAny class with access to the declaring type
protectedThe same package, plus subclass access across packages
No modifierThe same package
privateThe declaring class

Private members are not inherited. The private name field still exists inside each student, but Student cannot read it directly. Keeping the field private and providing a getter lets Person control its own data. Across packages, protected instance access has an extra restriction: subclass code accesses the member through itself or a reference of its own subclass type, not through an arbitrary superclass reference.

Test the relationship before extending

Java allows one direct superclass per class. A chain such as GraduateStudent extends Student and Student extends Person is valid; extends Student, Teacher is not. A class with no explicit superclass extends Object. Object itself has no superclass.

Use the is-a test: can every subclass object be used wherever the superclass is expected? A truck is a vehicle. A customer has an address, but a customer is not an address. For that second relationship, use composition:

class Customer {
    private Address address;
}

Here Address is another class. The field lets a customer use an address without claiming to be one. Extending a class just to borrow a convenient method also inherits its whole public contract. Finally, a final class cannot be extended. Because String is final, class ShortString extends String fails to compile.

Key Takeaways

  • A subclass object includes superclass state and any state the subclass adds.
  • Private superclass fields are accessed through the superclass's methods.
  • A class extends one direct superclass; constructors are not inherited.
  • Use inheritance for an is-a relationship and composition for a has-a relationship.
  • A final class cannot be extended.

1.2 Pass values through a constructor chain

A subclass constructor must arrange construction of the superclass part of the same object. It can call a superclass constructor with super(...), or delegate to another constructor in its own class with this(...).

Choose the superclass constructor
class Person {
    private final String name;
    Person(String name) { this.name = name; }
}
class Student extends Person {
    private final String studentId;
    Student(String name, String studentId) {
        super(name);
        this.studentId = studentId;
    }
    Student(String name) {
        this(name, "no id yet");
    }
}

The two-argument constructor passes the name to Person and stores the ID itself. The one-argument constructor delegates to it. A constructor contains at most one explicit constructor invocation: this(...) or super(...). Following the this(...) chain eventually reaches a constructor that invokes super(...).

If neither is written, Java inserts super(). That fails here because Person has only a constructor requiring a name. Declaring any constructor prevents Java from supplying a default no-argument constructor. A class with no declared constructor gets a default one that calls super().

Writing new Person(name) inside Student does something else: it constructs a separate object. It does not initialize the current student's person state or replace the required superclass constructor call.

Trace initialization in order
class Base {
    Base() { System.out.println("Base ready"); }
}
class Middle extends Base {
    Middle() { System.out.println("Middle ready"); }
}
class Top extends Middle {
    Top() { super(); System.out.println("Top ready"); }
}

new Top() enters the constructor chain up to Object. As calls return, the output is "Base ready", "Middle ready", then "Top ready". Each class's field initializers run after its superclass constructor returns and before that class's remaining constructor body.

An overridable method called by a superclass constructor can already run a subclass override. If that override reads a subclass reference field whose assignment has not run, it sees null, even if the field has a declaration initializer. Keep constructor work to setting up state; avoid calls to methods subclasses can override.

Recognize the Java 25 change

In Java 25, a constructor can validate parameters before its explicit constructor invocation:

Student(String name, String studentId, int credits) {
    if (credits < 0) {
        throw new IllegalArgumentException("negative credits");
    }
    super(name);
    this.studentId = studentId;
}

Java 25 also allows simple early assignments to fields declared in the current class without declaration initializers. It still forbids reading the current object's fields, calling its instance methods, or passing this around before the invocation. For standard language levels before Java 25, keep super(...) or this(...) first. Java 22–24 offered earlier forms as preview features.

Key Takeaways

  • super(...) constructs the superclass part of the current object.
  • An implicit super() needs an accessible no-argument superclass constructor.
  • this(...) delegates within the same class; its chain must reach a superclass constructor.
  • Field initializers run after the superclass constructor returns.
  • Java 25 permits restricted work before an explicit constructor invocation.
  • An override called during construction may observe subclass fields before their assignments.
2

Override methods and use the Object class

Inherited methods give related objects a shared starting point. Overriding lets each subclass answer the same call in its own way, while the methods inherited from Object control how an object prints and compares with others. Small signature differences matter here: a method can look right to a reader yet fail to replace the method library code actually calls. Equality also has promises that hash-based collections rely on.

2.1 Replace inherited behavior with an override

An override gives a subclass its own implementation of an inherited instance method. Its name and parameter types match the original method, so callers can keep using the same operation.

Extend a working method

Suppose an employee's yearly pay is twelve monthly payments. A manager also receives a yearly bonus:

class Employee {
    private final double monthlySalary;
    Employee(double monthlySalary) {
        this.monthlySalary = monthlySalary;
    }
    public double yearlyPay() { return 12 * monthlySalary; }
}
class Manager extends Employee {
    private final double bonus;
    Manager(double monthlySalary, double bonus) {
        super(monthlySalary);
        this.bonus = bonus;
    }
    @Override
    public double yearlyPay() {
        return super.yearlyPay() + bonus;
    }
}

For new Manager(3000.0, 5000.0), the superclass call returns 36000.0. Adding 5000.0 gives 41000.0. super.yearlyPay() deliberately selects the superclass implementation. The superclass method remains available inside the subclass even though ordinary calls on the manager run the override.

Writing return yearlyPay() + bonus; calls the manager's own method again. No call finishes, so execution eventually fails with StackOverflowError. The missing super. changes the meaning of the call.

Let the compiler check the signature

Put @Override above each overriding method. A misspelling such as yearlypay() or an extra parameter then causes a compile error. Without the annotation, Java may accept a new method that callers never use.

A valid override follows these rules:

  • Its parameter types match exactly.
  • Its return type is the same, or a subtype when returning an object type. That permitted narrowing is a covariant return type.
  • Its access is at least as open as the inherited method's access.
  • It does not add checked exceptions that the inherited declaration forbids.

For example, public String toString() cannot be replaced with package-private String toString(). Existing callers must retain access.

Separate overriding from overloading and hiding
DeclarationWhat it does
Same instance-method signature in a subclassOverrides inherited behavior
Same name, different parameter listAdds an overload
Same static-method signature in a subclassHides a static method

role() and role(String language) are overloads; the second does not replace the first. Overload choice uses declared argument types at compile time. Static methods are also selected from the type used at compile time, so they are not dynamically overridden.

A private method is not inherited; a same-named subclass method is independent. A final method cannot be overridden, and constructors cannot be overridden. Beyond compiling, the method must still fulfill its original purpose. A manager override that returns monthly pay would break code that totals yearly pay even if its signature were correct.

Key Takeaways

  • An override matches an inherited instance method's name and parameter types.
  • @Override catches accidental overloads and misspellings.
  • super.method() reuses the superclass implementation.
  • An override cannot reduce access or broaden the permitted checked exceptions.
  • Overloading changes parameters; static methods are hidden, and final methods cannot be overridden.

2.2 Give objects useful text and identify their class

Every Java class extends Object directly or indirectly. Arrays are objects too. This shared root provides methods such as toString(), equals(Object), hashCode() and getClass() even when a class declares none of them.

Replace the default printed description

The inherited Object.toString() produces a class name, @, and the object's hash code in hexadecimal. Text such as "school.Person@1b6d3586" identifies the class but does not explain the person's data. The number may change across runs and is not a memory address promised by Java.

A useful override shows the fields readers need. Here a student has inherited getName() and stores a credits field:

@Override
public String toString() {
    return getName() + " (" + credits + " credits)";
}

For a student named Ada Lovelace with 10 credits, System.out.println(s) prints "Ada Lovelace (10 credits)". The print call obtains the object's text automatically. String concatenation does this too: "Student: " + s includes the same description.

For a null reference, printing and concatenation produce "null". An explicit call s.toString() when s is null throws NullPointerException, because there is no object to receive that call.

If Person.toString() already returns the person's name, a subclass can build on it:

@Override
public String toString() {
    return super.toString() + ", student id " + studentId;
}

For ID "S-17", that produces "Ada Lovelace, student id S-17". The usefulness of super.toString() depends on what the superclass actually returns. If it still uses Object's version, it contributes the default class-and-hash text.

Keep identity and class information separate

Object.equals(Object) compares identity: it returns true when both references refer to the same object. Two separate new Person("Ada") objects are not equal under that inherited implementation, despite having matching names. Classes such as String, Integer and ArrayList supply value-based overrides.

getClass() reports the actual runtime class:

Person p = new Student("Ada Lovelace");
System.out.println(p.getClass().getSimpleName());

This prints "Student" when Student has the constructor shown. The variable's declared type does not alter the object's class. getClass() is final and cannot be overridden.

hashCode() supports hash-based collections. Other methods from Object include the protected clone() and the thread-coordination methods wait(), notify() and notifyAll().

Check exact spelling

Java distinguishes toString() from tostring(). A method toString(String prefix) is an overload, so printing does not call it. Both mistakes are caught by @Override. A text representation also does not define equality: two objects can print the same text while still comparing unequal.

Key Takeaways

  • Object supplies common methods to all Java objects, including arrays.
  • Printing and string concatenation use toString() and render a null reference as "null".
  • Object.equals compares identity until a class overrides it.
  • getClass() returns the actual class and is final.

2.3 Keep equality and hashing consistent

Two objects can represent the same value without being the same object. A book catalog can treat two book objects as equal when their catalog code and edition match. Define that rule by overriding equals(Object) and keep hashing consistent with it.

Compare the fields that define the value
import java.util.Objects;

final class Book {
    private final String code;
    private final int edition;
    Book(String code, int edition) {
        this.code = code;
        this.edition = edition;
    }
    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (!(other instanceof Book b)) return false;
        return edition == b.edition && Objects.equals(code, b.code);
    }
    @Override
    public int hashCode() {
        return Objects.hash(code, edition);
    }
}

The identity check accepts the same object quickly. The type check rejects an unrelated object or null, then introduces b as a Book. Primitive editions use ==; Objects.equals compares codes safely even when a code is null. Two books with code "B-1042" and edition 2 compare equal. Edition 3 makes a different value.

The parameter must be Object. Writing equals(Book other) adds an overload; it leaves the inherited equals(Object) in place. Library code calling that signature would still compare identity. @Override makes the wrong declaration fail at compile time.

Follow the equality promises

For non-null references, equality must be:

  • Reflexive: an object equals itself.
  • Symmetric: switching the two objects gives the same answer.
  • Transitive: if one equals a second and the second equals a third, the first equals the third.
  • Consistent: repeated comparisons agree while the compared data stays unchanged.

An object must also compare unequal to null. Equal objects must return equal hash codes. Unequal objects are allowed to share a hash code: a hash collision does not establish equality. Derive the hash from the same fields as equality, as Objects.hash(code, edition) does here.

A hash collection uses the hash to locate candidates before checking equality. Overriding only equals can make a set retain separate equal books or make a map fail to find an equal key. Changing a field used in hashing while its object is stored as a key can leave that object filed under its old hash.

Protect symmetry across subclasses

Suppose an extensible Book accepts any instanceof Book, while Ebook adds file size to equality and accepts only ebooks. A book may accept an ebook with the same code and edition, but the ebook rejects the book. The comparison has lost symmetry.

One approach requires matching runtime classes, checking null before getClass(). Another makes superclass equality final so subclasses cannot extend the equality rule. A final value class, as above, avoids subclass equality entirely. Records generate equality and hashing from their components; enum constants already have identity-based equality suited to their fixed values.

Key Takeaways

  • Override equals(Object), not an overload with the class as its parameter.
  • Equality must be reflexive, symmetric, transitive and consistent; comparison with null is false.
  • Equal objects need equal hash codes; a shared hash does not establish equality.
  • Keep equality fields stable while an object is a hash collection key or set element.
  • A superclass and subclass must not use equality rules that disagree when the operands are reversed.
  • Records and enums supply suitable built-in equality behavior.
3

Define contracts with abstract classes and interfaces

Some related classes share fields and working methods; other classes only share a capability. Abstract classes and interfaces express those different relationships. A drawing program can keep common shape data in an abstract class while a charging program works with unrelated devices through an interface. Both approaches let the compiler check required behavior before the program runs, and Java provides precise rules when inherited implementations compete.

3.1 Share state and require subclass behavior

An abstract class is a base class that cannot be instantiated directly. It can hold common fields and working methods while leaving selected operations for subclasses to complete.

Leave a required operation abstract
abstract class Shape {
    private final String name;
    Shape(String name) { this.name = name; }
    public abstract double area();
    public String describe() {
        return name + " with area " + area();
    }
}
class Rectangle extends Shape {
    private final double width;
    private final double height;
    Rectangle(double width, double height) {
        super("rectangle");
        this.width = width;
        this.height = height;
    }
    @Override
    public double area() { return width * height; }
}

area() has a signature and a semicolon, with no body. It states what every concrete shape must provide. Because Shape contains an abstract method, the class must be abstract too. Rectangle supplies the missing implementation and can be instantiated.

For new Rectangle(3.0, 4.0), the superclass constructor stores "rectangle". The subclass constructor stores the dimensions. Its area is 12.0 square meters when dimensions are in meters. Calling describe() returns "rectangle with area 12.0". The inherited method's call to area() runs the rectangle's implementation.

Use constructors and concrete methods normally

An abstract class can have fields, constructors, and methods with bodies, with the usual access levels. Its constructor runs as part of creating a subclass object. new Shape() is forbidden even if a matching constructor exists; new Rectangle(3.0, 4.0) is valid.

A second subclass can use the same inherited description while calculating area differently. A circle implementation returns Math.PI * radius * radius. For radius 1.5 m, the calculation is π times 2.25, about 7.07 square meters. The shared describe() method needs no circle-specific branch.

A variable can still have the abstract type: Shape s = new Rectangle(3.0, 4.0);. Java forbids constructing the abstract class, not referring to a concrete object through it.

Make missing work a compile error

Every concrete subclass must provide or inherit implementations of all abstract methods. A subclass that leaves some unfinished must also be declared abstract. Returning 0 from a placeholder area() would let an incomplete concrete subclass compile and silently give wrong results. Declaring area() abstract makes that missing work visible to the compiler.

Abstract methods cannot be private, static or final: each would prevent the subclass override that supplies the operation. An abstract class may contain no abstract methods; the modifier then only prevents direct construction. Use this kind of base class when its subclasses pass the is-a test and benefit from shared state or code.

Key Takeaways

  • An abstract class cannot be constructed directly, but its constructor runs for subclass objects.
  • An abstract method has no body and requires a concrete implementation.
  • A concrete subclass must complete every inherited abstract method.
  • An abstract type can be used for variables, parameters and lists.
  • Abstract classes can combine common fields, working methods and required operations.

3.2 Give unrelated classes a shared contract

An interface names a capability that several classes can provide. It states the methods available through that capability without requiring the classes to share the same instance fields or superclass.

Declare and implement the methods
interface Chargeable {
    int batteryPercent();
    void charge(int minutes);
}
class Phone implements Chargeable {
    private int battery;
    Phone(int battery) { this.battery = battery; }
    @Override
    public int batteryPercent() { return battery; }
    @Override
    public void charge(int minutes) {
        battery = Math.min(100, battery + 2 * minutes);
    }
}

The interface's abstract methods are implicitly public. The implementing methods must explicitly be public; omitting that modifier would reduce access and fail compilation. A concrete class must supply or inherit suitable public implementations of every abstract method. An abstract implementing class may leave some for its subclasses.

For a phone at 35 percent, charge(20) adds 40 percentage points and leaves 75 percent. Another 20 minutes would reach 115 before the cap, so Math.min stores 100. An ElectricScooter can implement the same interface while adding only one point per minute: starting at 50 percent, it reaches 70 after 20 minutes. The interface fixes the available operation, while the class supplies its behavior.

Use the interface as a type
Chargeable device = new Phone(35);
device.charge(20);
System.out.println(device.batteryPercent());

This prints 75. A caller can use the interface's methods and the public methods available on every object. Phone-only operations are not available through device unless the declared interface exposes them.

Interfaces can also be parameter, return, and list element types. A method accepting Chargeable can work with a phone or a scooter without knowing their field layouts. Java's library uses the same approach: List has implementations such as ArrayList and LinkedList; Runnable declares run(); Comparable declares natural comparison.

Combine more than one capability

A class extends one class and may implement several interfaces. The order is class Phone extends Device implements Chargeable, Connectable. An interface can extend several interfaces: interface SmartDevice extends Chargeable, Connectable. Implementers then owe all the inherited abstract operations.

An interface has no constructors and no instance fields. new Chargeable() does not compile. A declared field such as int FULL = 100; is implicitly a public static final constant, read as Chargeable.FULL.

An abstract interface method contributes no working code. Its implementation may come from the class itself or an inherited public superclass method. Default methods are the interface methods that can supply inherited instance behavior.

Key Takeaways

  • An interface is a type describing a shared capability.
  • A concrete implementing class must provide or inherit suitable public implementations.
  • A class can implement several interfaces while extending one class.
  • Interfaces have no constructors or instance fields; their fields are constants.
  • The interface type lets callers use its contract across unrelated classes.

3.3 Use shared interface behavior and resolve conflicts

Interfaces can supply working code as well as abstract method declarations. The method's modifier decides whether implementing objects inherit it, callers use the interface name, or only code inside the interface can use it.

Build default behavior from the contract
interface Chargeable {
    int batteryPercent();
    void charge(int minutes);
    default boolean needsCharge() {
        return batteryPercent() < 20;
    }
    static int clamp(int percent) {
        return Math.max(0, Math.min(100, percent));
    }
}

A default method is a public instance method with a body. A phone reporting 15 percent inherits needsCharge() and returns true. At 20 percent it returns false. The default calls the abstract batteryPercent() operation; it does not read a phone's private field. A class may override the method with its own public implementation.

A static interface method belongs to the interface: Chargeable.clamp(130) returns 100 and Chargeable.clamp(-5) returns 0. Implementing classes do not inherit it, so Phone.clamp(130) is not a valid call to this method. Default and static interface methods were introduced in Java 8.

Since Java 9, an interface may also declare private methods to share helper code among its own methods. A private instance helper supports default methods; a private static helper can also support static methods. Implementing classes cannot call these helpers directly.

Resolve competing defaults

Two unrelated interfaces can supply the same signature:

interface Lamp {
    default String describe() { return "lamp"; }
}
interface Speaker {
    default String describe() { return "speaker"; }
}
class SmartLamp implements Lamp, Speaker {
    @Override
    public String describe() {
        return Lamp.super.describe() + " and "
             + Speaker.super.describe();
    }
}

Without the override, SmartLamp has a conflict and does not compile. The qualified InterfaceName.super.method() calls select the individual defaults from its direct superinterfaces. This implementation returns "lamp and speaker".

Competing implementationsRule
Inherited public concrete superclass method and interface defaultThe class method wins
Default from an interface and its extending interfaceThe more specific interface wins
Defaults from unrelated interfacesThe class must resolve the conflict

An abstract superclass declaration can also require a concrete subclass to implement the method; an interface default does not remove that obligation.

Consider existing implementers

Adding an abstract method requires each concrete implementer to provide or inherit a matching implementation. A default often avoids that change by supplying working behavior, but it can introduce a conflict with another inherited method. Defaults help an interface evolve; the inheritance rules still determine whether the resulting classes compile.

Key Takeaways

  • A default method supplies overridable public instance behavior.
  • Call a static interface method through the interface name.
  • Private interface methods are helpers visible only inside the interface.
  • A concrete class method takes priority over an interface default.
  • Unrelated competing defaults require an override; the more specific interface default otherwise wins.
  • Adding a default can introduce inherited-method conflicts.

3.4 Choose a shared base, a capability, or both

An abstract class and an interface can both define required methods. The choice depends on the relationship between objects and whether their implementation needs shared instance state.

Compare what each one supplies
QuestionAbstract classInterface
Can it store per-object fields?YesNo
Can it have constructors?YesNo
Can it supply working instance methods?YesYes, with default methods
How many can a class directly use?One superclassSeveral implemented interfaces
Which access levels are available?All normal levelsPublic methods and private helpers

Interface fields are public static final constants, not per-object data. Private interface helpers support its own working methods; they do not create private state for each implementing object.

Put closely related state in an abstract class

A drawing program has circles, rectangles and triangles. Each shape stores a name and a position, and each computes its area differently. An abstract Shape can keep the common fields and constructor, provide working movement or description methods, and declare area() abstract.

All concrete shapes are kinds of Shape, so other code can ask for a shape and receive any of them. Similarly, an abstract Enemy might own health points and a shared takeDamage(int points) method while leaving movement to particular enemy classes. The shared state gives these base classes useful work to do.

Put a cross-cutting capability in an interface

The same drawing program can export shapes, text labels and images. Exporting does not make a text label a shape, and these objects may already need different superclasses. An Exportable interface can declare String exportText(). Each class implements the operation using its own data.

A class may combine the two roles: class Circle extends Shape implements Exportable. The superclass provides shape state and shared code; the interface makes the circle usable by export code. A class implementing several interfaces promises several capabilities without inheriting instance fields from them.

Combine a contract with a partial implementation

An abstract class can implement an interface and complete only some methods. Concrete subclasses finish the remaining operations. Java's List and AbstractList use this arrangement; ArrayList extends AbstractList. Callers can depend on the interface while implementers reuse a base class where it fits.

For example, List<String> names = new ArrayList<>(); lets callers add, read and remove names through List. Replacing the initializer with new LinkedList<>() preserves those calls. A parameter declared as ArrayList<String> would reject a LinkedList<String> even when it only needs list operations.

An abstract class containing only abstract methods still occupies the subclass's single superclass slot. It is not interchangeable with an interface. Choose the interface when only the common contract is needed, and add a base class when related implementations benefit from shared state or code.

Key Takeaways

  • Use an abstract class for related kinds of objects that share state or code.
  • Use an interface for a capability that unrelated classes can provide.
  • A class can extend an abstract base and implement interfaces together.
  • A partial abstract implementation can support an interface without restricting callers to that base class.
4

Write flexible programs with polymorphism

A variable's type and an object's class answer different questions. The variable's declared type controls which calls compile; the object's actual class controls which override runs. Keeping those decisions separate makes it possible to trace unfamiliar code, use casts safely, and write loops that work with new implementations. The same approach supports library interfaces such as Comparable, which gives objects a natural sorting order.

4.1 Separate the reference type from the object class

Polymorphism lets a general reference point to objects of different subtypes. The object keeps its full state and behavior; the reference gives callers a particular view of the available operations.

Name the two types
Person p = new Student("Ada Lovelace", "S-17");

The declared type, also called the static type, is Person: it appears in the variable declaration. The actual type, or runtime type, is Student: that is the class instantiated by new. The object's actual class does not change after construction.

Assigning a student reference to a person reference is an upcast. It is automatic and safe because every student is a person. The same student can also be referred to by an Object variable. An implementing object can be upcast to an interface it implements, as in Chargeable c = new Phone(35);.

No student data is deleted by the assignment. There is still one student object with its student ID, inherited person data and overridden methods. Giving it a more general reference changes what the compiler lets that reference access.

Check calls against the declared type

If Person declares getName(), then p.getName() compiles. If only Student declares getStudentId(), then p.getStudentId() does not compile. The compiler checks the Person API even when the initializer clearly creates a student.

The reverse assignment Student s = p; is not accepted without a cast. A person reference could point to a non-student person. That restriction protects code from treating an object as a subtype it does not have.

Declared typeCalls available through the reference
ObjectPublic operations provided by Object
PersonAccessible Person operations, including inherited ones
StudentAccessible student operations and inherited operations
Let parameters and lists accept several subtypes

A method static void printCard(Person person) can receive a Person, Student or Teacher object, assuming those last two extend Person. Its code can rely on the person contract. A return type can be general too, for example a method that returns a Shape.

A list with a general element type can store different shape classes:

List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(1.5));
shapes.add(new Rectangle(3.0, 4.0));
for (Shape shape : shapes) {
    System.out.println(shape.area());
}

Here List and ArrayList come from java.util, and each concrete shape supplies area(). The loop uses only that common operation.

A List<Circle> is not a List<Shape>. If it were, code receiving it as a shape list could add a rectangle into a list promised to contain only circles. When a method requires List<Shape>, create the list with that element type from the start.

Key Takeaways

  • The declared type controls which operations a reference can access.
  • The actual type is the class of the object and does not change after construction.
  • Upcasting to a superclass or implemented interface is automatic and safe.
  • A general parameter or list element type can support several concrete subtypes.
  • List<Circle> cannot be used as List<Shape>.

4.2 Trace overload selection and dynamic binding

A method call is resolved in two stages. At compile time, Java checks the declared types and chooses a method signature. At run time, dynamic binding chooses the overriding instance-method implementation for the object's actual class.

Follow a call inside an inherited method
class A {
    String who() { return "A"; }
    String hello() { return "Hello from " + who(); }
}
class B extends A {
    @Override String who() { return "B"; }
}
class C extends B { }

For A x = new C();, x.hello() is allowed because A declares hello(). Class C has no override of hello(), nor does B, so Java runs the implementation in A.

Inside that method, who() means this.who(). The receiver is still the C object. Java searches from C, then finds the nearest who() implementation in B. The result is "Hello from B". Entering an inherited method does not turn the receiver into an object of the superclass.

Choose an overload before choosing an override
static String kind(Object value) { return "object"; }
static String kind(String value) { return "string"; }

Inside a class containing these methods, Object value = "hi"; kind(value) selects kind(Object) and returns "object". The variable's declared type is Object; Java does not reconsider overloads using the actual string at run time.

For an instance call, runtime dispatch can still choose an override of the already-selected signature. It cannot switch to a different overload that merely looks more specific for the actual argument. First identify the signature, then look for its override.

Recognize calls and members that do not dispatch this way
Member or callWhat selects it
Overridden instance methodActual receiver class, after signature selection
Static methodCompile-time type used for the call
Hidden fieldDeclared type of the reference
Private methodIts declaring class's code
super.method()Superclass implementation

If P declares String label = "P"; and Q extends P declares its own String label = "Q";, then P p = new Q(); p.label reads "P". Fields are hidden, not overridden. Both fields exist in the object. Avoid reusing inherited field names because the reference type can make identical-looking accesses read different state.

super.method() selects the superclass implementation, including one that superclass inherits. If the selected method itself makes an ordinary call on this, that inner call can still dispatch to a subclass override.

Key Takeaways

  • Compile time fixes the callable signature, including overload choice.
  • Dynamic binding selects the nearest override of that signature from the actual class upward.
  • An ordinary call inside an inherited method still uses the actual receiver.
  • Fields, static methods, private methods and explicit super calls do not use ordinary override dispatch.

4.3 Check a subtype before using its special methods

A general reference sometimes needs access to an operation that belongs only to one subtype. A downcast tells the compiler to treat the reference as that subtype. At run time, Java checks whether the object already has that type.

Understand what a cast changes

Suppose Person p refers to a student. Student s = (Student) p; allows s.getStudentId(). Both variables still refer to the same object. The cast does not copy it, change its actual class, or add student fields to a plain person.

If p actually refers to new Person("Grace Hopper"), that cast throws ClassCastException. A compiler accepts the possible person-to-student relationship, but the runtime object must still meet it. A cast between classes that cannot be related, such as a Person reference cast to the final class Integer, fails at compile time.

This differs from a primitive conversion: (int) 3.9 creates the integer value 3. A reference cast only checks and changes the type through which an existing reference is used.

Combine the test and the new variable

Since Java 16, pattern matching makes the safe case compact:

static void showStudentId(Person person) {
    if (person instanceof Student student) {
        System.out.println(student.getStudentId());
    }
}

The test is true for a student or a subclass of student. The variable student is available inside the block, where the test is known to have succeeded. A plain person skips the block. A null reference also makes the test false, so the block never tries to call a method on null.

The older form tests person instanceof Student and then declares Student student = (Student) person; inside the successful branch. Both forms protect the cast. Testing an unrelated object elsewhere does not protect a cast of person; the test and cast must concern the same reference.

An upcast needs no explicit cast. Person person = student; already compiles and cannot fail from a type mismatch. A cast of null to a compatible reference type produces null; using that result for an instance-method call still fails with NullPointerException.

Decide whether a type test is needed

Type tests fit code that receives an Object, such as equals(Object other), or needs a subtype-only operation such as a student ID. They can also help when working with classes that cannot be changed.

A repeated type-test chain for a shared operation is a different problem. If code checks for each shape class to calculate its area, every added shape requires another branch. Declaring area() on Shape and letting subclasses implement it lets the caller use shape.area() instead.

A cast cannot make a missing common operation available across every subtype. Put shared behavior in the common contract, and reserve a subtype test for work that actually requires that particular subtype.

Key Takeaways

  • A downcast checks an existing object; it does not transform the object.
  • A possible cast can compile and still throw ClassCastException at run time.
  • instanceof is false for null and true for instances of the type or its subtypes.
  • An instanceof pattern introduces a safely typed variable where the test is known true.
  • Shared operations belong in the common type; subtype-only operations may need a type test.

4.4 Write general loops and define a natural order

A method stays useful across new classes when its parameter exposes exactly the operations the method needs. The caller supplies objects; dynamic binding supplies their individual behavior.

Add areas without class-specific branches
static double totalArea(List<Shape> shapes) {
    double total = 0;
    for (Shape shape : shapes) {
        total += shape.area();
    }
    return total;
}

With a circle of radius 1.5 m, a rectangle of dimensions 3.0 m and 4.0 m, and a triangle of base 4.0 m and height 2.5 m, the areas are about 7.06858, 12.0 and 5.0 square meters. Adding the unrounded circle area gives about 24.07 square meters. Each object supplies its own area().

Adding a Triangle class does not require editing the loop. The new class implements the existing shape operation. A negative result would violate the area contract even if the override compiled; general code relies on what the operation means as well as its signature.

The same approach works with an interface. A chargeAll(List<Chargeable> devices, int minutes) method loops over devices and calls device.charge(minutes). For 20 minutes, the phone starting at 35 percent reaches 75 percent and the scooter starting at 50 percent reaches 70 percent. The loop never selects a charging rule itself.

Let callers choose a list implementation

A parameter List<Shape> accepts both an ArrayList<Shape> and a LinkedList<Shape>. A parameter ArrayList<Shape> rejects the linked list despite needing only list operations. Use the most general type that supplies the required methods; declaring everything as Object would be too general because it lacks area() and list operations.

Make objects sortable with Comparable

Comparable<T> defines an object's natural order. Its compareTo result is negative when this object comes before the other, zero for an ordering tie, and positive when it comes after. The result's sign matters; it need not be exactly −1 or 1.

record Runner(String name, int seconds)
        implements Comparable<Runner> {
    @Override
    public int compareTo(Runner other) {
        return Integer.compare(seconds, other.seconds);
    }
}

A modifiable list with Mia at 3605 s, Ken at 3490 s and Ana at 3720 s becomes Ken, Mia, Ana after Collections.sort(runners). list.sort(null) also uses natural ordering. Build a modifiable list with new ArrayList<>(List.of(...)); attempting to sort the unmodifiable result of List.of(...) throws UnsupportedOperationException.

Use Integer.compare instead of subtracting the values. Subtraction can overflow for large values of opposite signs and return the wrong sign. The same idea applies to Double.compare for decimal fields. In this runner example, equal times tie in ordering even if the names differ, so a comparison tie and object equality need not mean the same thing. Prefer a natural order whose zero result agrees with equality; document an intentional difference.

Key Takeaways

  • General code calls a shared operation and lets each object supply its implementation.
  • An interface parameter can accept different implementations of the same contract.
  • compareTo uses a negative, zero or positive result to express ordering.
  • Integer.compare avoids the overflow risk of subtracting integer fields.
  • Sorting requires a modifiable list; List.of(...) alone is unmodifiable.
  • An ordering tie can differ from value equality when comparison uses fewer fields.
5

Model fixed values and data with enums and records

Some programs need a fixed set of choices, while others need a small group of values that belongs together. Enums give choices a type of their own; records supply much of the routine code for data objects. Both fit Java's type system and can implement interfaces. Their built-in behavior saves work, but choosing between them and an ordinary class still depends on what can change and what makes two values equal.

5.1 Represent fixed choices with an enum

An enum gives a fixed set of named choices its own type. enum Size { SMALL, MEDIUM, LARGE } allows Size size = Size.MEDIUM;. It prevents a caller from passing the misspelled string "MEDUIM" or the integer 7 to a method that requires Size.

Use names, constants and switch

Each enum constant is a single object, so size == Size.LARGE is a suitable comparison. An enum variable can also be null. Size.values() returns all constants in declaration order; Size.valueOf("LARGE") finds the exact name. The lookup is case-sensitive: "large" causes IllegalArgumentException.

name() returns the declared name. ordinal() returns the zero-based position, so Size.MEDIUM.ordinal() is 1. Inserting a constant changes later positions, so an ordinal is a poor stored code or business value. By default, toString() also returns the name.

A switch expression, standard since Java 14, can return a value:

String label = switch (size) {
    case SMALL -> "S";
    case MEDIUM -> "M";
    case LARGE -> "L";
};

Naming every constant makes the expression exhaustive without a default branch. These cases assume a non-null size; listing the constants does not handle null.

Give each constant meaningful data
enum CupSize {
    SMALL(250), MEDIUM(350), LARGE(500);
    private final int milliliters;
    CupSize(int milliliters) { this.milliliters = milliliters; }
    public int milliliters() { return milliliters; }
}

The semicolon separates the constant list from the members. The constructor runs once for each constant when the enum initializes. It is implicitly private, and callers cannot create new constants with new CupSize(400).

Two large cups and one small cup hold 1250 mL, or 1.25 L. That calculation uses the milliliters field, not the constants' positions. Looping over CupSize.values() and printing each constant and capacity gives "SMALL: 250 mL", "MEDIUM: 350 mL" and "LARGE: 500 mL".

Give constants their own behavior

An enum can implement an interface, and it may give individual constants different method bodies:

enum Operation {
    ADD { int apply(int a, int b) { return a + b; } },
    MULTIPLY { int apply(int a, int b) { return a * b; } };
    abstract int apply(int a, int b);
}

Operation.MULTIPLY.apply(6, 7) returns 42. Each constant completes the abstract operation. Enums implicitly extend java.lang.Enum; they cannot extend another class, and ordinary classes cannot extend them.

Use an enum for options fixed when the program is written, such as directions or order states. A shop's changing list of products belongs in data: adding products should not require rewriting and recompiling an enum declaration.

Key Takeaways

  • An enum gives a fixed set of constants a distinct type.
  • values() preserves declaration order and valueOf matches the exact name.
  • Store meaningful numbers in fields, because ordinal positions can change.
  • Enum constructors initialize constants; callers cannot use new to add constants.
  • Enums can implement interfaces and give constants different behavior.

5.2 Carry values with a record and validate them

A record is a concise data class, standard since Java 16. Its header lists the values, called components, that define each instance: record Point(int x, int y) { }.

Use the generated members

The compiler supplies a private final field for each component, a canonical constructor with those components in order, and public accessors named after them. It also supplies equals, hashCode and toString based on the components.

Point a = new Point(3, 4);
Point b = new Point(3, 4);
System.out.println(a.x());
System.out.println(a.equals(b));
System.out.println(a == b);

This prints 3, true and false on separate lines. Matching component values make the records equal, but the two new expressions create separate objects. Equal records have equal hash codes. Their useful generated text includes the record and component names; current JDKs show "Point[x=3, y=4]". That exact text format is not guaranteed, so do not parse it as a data format.

Accessors are x() and y(), not getX() and getY(). A setter that assigns this.x = x does not compile because the component field is final. A method can return a new value instead: Point moveRight(int dx) { return new Point(x + dx, y); }.

Validate in a compact constructor
record Temperature(double celsius) {
    Temperature {
        if (celsius < -273.15) {
            throw new IllegalArgumentException("below absolute zero");
        }
    }
}

A compact constructor repeats the record's name without a parameter list. It checks or normalizes the incoming parameters; Java assigns the component fields after its body. This check rejects −300 °C and accepts 21.5 °C. A text component could be normalized with name = name.trim(); before automatic assignment.

A public record needs a public compact constructor. Records may also declare static fields, static methods and instance methods. An additional constructor delegates to the canonical constructor with this(...). Extra instance fields beyond the components are forbidden.

Protect mutable component objects

Final component fields make immutability shallow. A record holding a mutable list still refers to that list:

record Team(String name, List<String> members) {
    Team {
        members = List.copyOf(members);
    }
}

With java.util.List imported, this stores an unmodifiable copy. Later additions to the original list do not affect the team's membership, and team.members().add("Lee") throws UnsupportedOperationException. The copy protects the list structure; it does not make mutable element objects deeply immutable.

Records implicitly extend java.lang.Record and are final. They cannot extend another class or be extended, but can implement interfaces such as Comparable. Use them for data defined by its values; use an ordinary class for changing state, extra hidden instance fields or an extensible class hierarchy.

Key Takeaways

  • A record header defines components and the generated constructor, accessors and value methods.
  • Record accessors use component names such as x().
  • A compact constructor checks or normalizes parameters before automatic field assignment.
  • Record component references are final, but referenced objects may remain mutable.
  • An unmodifiable list copy protects a record's list structure.
  • A record is final, cannot extend a class explicitly, and can implement interfaces.

Java Inheritance and Polymorphism examples

5 Walkthroughs

1

Build an inherited device step by step

12 steps
Create a labeled device, add a sensor subtype and follow construction through the same object.
  1. 1
    IntroductionOne object with a general and a specific role

    A sensor is a kind of device. Every device needs a label, while a sensor also needs a measurement unit. Inheritance lets the device own its label and the sensor add its unit.

    We will build that relationship, use an inherited method, and trace how constructor calls initialize each part. The key question is which class owns each field and therefore which constructor should assign it.

  2. 2
    ExampleStart with the state every device needs
    class Device {
        private final String label;
        Device(String label) { this.label = label; }
        public String label() { return label; }
    }
    

    Device controls its private label. Other code reads it through label(). A subclass will inherit that public method, but it cannot assign the private field directly. That keeps label initialization in one place.

  3. 3
    ExampleAdd only the sensor-specific state
    class Sensor extends Device {
        private final String unit;
        Sensor(String label, String unit) {
            super(label);
            this.unit = unit;
        }
        public String unit() { return unit; }
    }
    

    Sensor extends one class and adds one field. Its super(label) invocation passes responsibility for the label to Device; the sensor constructor stores the unit itself.

  4. 4
    Key InsightRead both parts through the sensor

    After Sensor s = new Sensor("Room", "C");, s.label() returns "Room" and s.unit() returns "C". The first method is inherited; the second belongs to Sensor.

    Both calls concern the same object. Extending Device did not create a second device object beside the sensor. The private label is present, even though sensor code accesses it through a method.

  5. 5
    ChallengeChoose the relationship for a lab

    A Lab contains several sensors. Should Lab extend Sensor, or keep references to sensors? Explain the relationship.

    ---reveal---

    Keep sensor references in fields or a collection. A lab has sensors but is not itself a sensor, so composition fits. Extending Sensor would promise that a lab can be used wherever a sensor is required, including operations that describe one sensor's unit.

  6. 6
    ExplanationEnter the superclass constructor

    Calling new Sensor("Room", "C") selects the two-argument sensor constructor. Its super(label) invokes Device(String) for that same object.

    Device first reaches its own superclass, Object, then assigns the label. When Device returns, sensor field initialization and the remaining sensor constructor body run. Finally, the new reference becomes available to the caller.

  7. 7
    TipFind the missing no-argument constructor

    Remove super(label) from the sensor constructor and Java inserts super(). That call has no match: Device declares only Device(String).

    The constructor error is about arguments, not about the private field. Replacing the invocation with new Device(label) creates an unrelated object and still leaves the implicit super() problem. Pass the label to the superclass constructor instead.

  8. 8
    ExampleDelegate an optional default unit

    Add this constructor inside Sensor:

    Sensor(String label) {
        this(label, "unknown");
    }
    

    new Sensor("Hall") delegates to the existing two-argument constructor, which invokes super(label). The resulting object has label "Hall" and unit "unknown". Do not also write super(...) in the delegating constructor: one constructor has at most one explicit constructor invocation.

  9. 9
    ConnectionValidate a parameter before construction

    Standard Java 25 permits restricted work before an explicit constructor invocation. For example, a sensor constructor can reject an empty incoming label before calling super(label).

    That early code may use parameters, but cannot read the object's fields or call its instance methods. Restricted simple assignments to current-class fields without declaration initializers are also allowed. For standard older language levels, keep the invocation first.

  10. 10
    ChallengePredict a constructor message

    In a separate version, Device prints "device" after its own superclass call. Sensor prints "sensor" after super(label). Which message appears first when creating a sensor, and why?

    ---reveal---

    "device" appears first. The sensor constructor invokes the device constructor before continuing to its own print statement. The object is a sensor throughout construction; these messages show the order of constructor work, not changes to its class.

  11. 11
    ChallengeRepair an inherited-field assignment

    A new subclass tries to initialize the private device label with this.label = label; and omits super(label). What two related problems does this create?

    ---reveal---

    The private field cannot be assigned directly by the subclass. The implicit super() also has no matching device constructor. Use super(label) so Device assigns its own private state, then assign only fields declared in the subclass.

  12. 12
    RecapLocate a construction failure

    When a subtype cannot be built, locate the first constructor call without a matching accessible target. Then check that each class initializes the fields it owns.

    Keep the relationship test alongside that trace: a subtype is usable as its base type; a contained object belongs in a field. These checks address different kinds of design and compiler errors.

2

Print and compare catalog entries correctly

14 steps
Override behavior, build a value-equality method and connect it to hashing.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose what a catalog entry should mean
  2. 2
    ExampleExtend a basic description
  3. 3
    TipAvoid calling the override from itself
  4. 4
    ChallengeCheck an apparent replacement
  5. 5
    ExampleGive printing an exact method to call
  6. 6
    Key InsightRead the actual class without changing it
  7. 7
    ChallengeDiagnose default-looking printed text
  8. 8
    ExampleDeclare the equality data
  9. 9
    ExampleReject the wrong object before comparing fields
  10. 10
    ExampleHash the same equality fields
  11. 11
    ChallengeSpot the overload that a collection misses
  12. 12
    Key InsightProtect comparisons in both directions
  13. 13
    ChallengeSeparate equal text from equal values
  14. 14
    RecapCheck a value class at its public boundary
3

Build a contract and share its implementation

18 steps
Use an abstract source, add an interface and resolve a conflict between inherited defaults.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSeparate the common data from the common capability
  2. 2
    ExampleStart an abstract message source
  3. 3
    ExampleComplete the missing operation
  4. 4
    ChallengeDecide whether a subclass can be concrete
  5. 5
    ExampleName reading as a capability
  6. 6
    ConnectionMake the existing base fulfill the interface
  7. 7
    ExampleWrite a caller against the contract
  8. 8
    ChallengeRepair an implementation with weaker access
  9. 9
    ExampleAdd a useful default operation
  10. 10
    ExplanationKeep static and private helpers in their roles
  11. 11
    ExampleCreate a genuine default conflict
  12. 12
    ExampleChoose a default explicitly
  13. 13
    ChallengeGive the class implementation its priority
  14. 14
    Key InsightOffer a base without requiring every implementation to use it
  15. 15
    ChallengePlace a label and a constructor in the right type
  16. 16
    TipCheck new defaults against inherited methods
  17. 17
    ChallengeAdd a new reader without changing its existing superclass
  18. 18
    RecapEvaluate a proposed common type
4

Trace a report call through general types

17 steps
Follow one receiver through overload choice, runtime dispatch and a safe subtype test, then build a general loop.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionFollow what the variable promises and what the object does
  2. 2
    ExampleGive a specialized report a general reference
  3. 3
    ExplanationCheck what the reference exposes
  4. 4
    ChallengeChoose types for a mixed report list
  5. 5
    ExampleResolve the outer call first
  6. 6
    ExampleResolve the inner call on the same receiver
  7. 7
    ExampleChoose an overload from the declared argument
  8. 8
    ChallengeSeparate two decisions in one call
  9. 9
    TipMark fields and static calls separately in a trace
  10. 10
    ExampleAsk safely for the sales-only operation
  11. 11
    ChallengePredict a cast when the object changes
  12. 12
    ExampleGather headings through the common operation
  13. 13
    ConnectionUse comparison as another shared operation
  14. 14
    ChallengeSort values through an implementation choice
  15. 15
    Key InsightKeep subtype checks for subtype work
  16. 16
    ChallengeSupport an audit heading without a new branch
  17. 17
    RecapTrace an unfamiliar call in order
5

Build a fixed choice and a protected data value

15 steps
Add enum data, create a record and prevent mutable list components from changing behind its back.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose between named options and grouped data
  2. 2
    ExampleGive service choices their own type
  3. 3
    ExampleAttach useful numbers to the choices
  4. 4
    TipKeep the service data independent of declaration order
  5. 5
    ChallengePredict lookup and a useful field
  6. 6
    ConnectionKeep behavior with the fixed choice
  7. 7
    ExampleDeclare the components of a delivery request
  8. 8
    ExampleNormalize incoming data before assignment
  9. 9
    ChallengeCompare the normalized requests
  10. 10
    ExampleFind the mutable object inside a final component
  11. 11
    ExampleStore an unmodifiable copy
  12. 12
    TipReturn a new record when values need to change
  13. 13
    ChallengeChoose the right repair for a changing route
  14. 14
    ChallengeModel new service options and customer requests
  15. 15
    RecapInspect generated behavior before adding code

Java Inheritance and Polymorphism practice questions

50 Practice Questions in 5 sets

Set 1: Build classes and trace inherited behavior10 Practice Questions

Question 1 of 10

A Library stores several Shelf objects, and each shelf stores books. How should Library represent its relationship with Shelf?
  1. Extend Shelf to inherit shelf methods.
  2. Store shelves in fields or a collection.
  3. Make Shelf a subclass of Library.
  4. Make Library a final subclass of Shelf.
Show answerHide answer
Answer: B
A library has shelves, so fields or a collection model the relationship through composition. Extending Shelf would also claim that every library is a shelf and could be used wherever a shelf is expected. Reusing shelf methods does not make that is-a relationship true.

Question 2 of 10

Account declares a private field balance and a public method getBalance(). SavingsAccount extends Account. How can a method in SavingsAccount read the inherited account state?
  1. Read the field as this.balance.
  2. Read the field as super.balance.
  3. Create an account and read its balance.
  4. Call the inherited getBalance() method.
Show answerHide answer
Answer: D
The public getter is inherited and can read the private field within Account. Private state still belongs to the savings object, but subclass code cannot access that field directly. Adding super. does not bypass the private access rule.

Question 3 of 10

What happens when this code is compiled?

class Machine {
    Machine(int serial) { }
}
class Printer extends Machine {
    Printer() { }
}
  1. Compilation fails because super() has no matching constructor.
  2. Compilation succeeds and uses Machine(0) by default.
  3. Compilation succeeds without constructing the machine part.
  4. Compilation fails because subclasses cannot have no-argument constructors.
Show answerHide answer
Answer: A
The empty printer constructor implicitly calls super(). Machine declares only a constructor taking an integer, so no no-argument constructor exists. Java does not invent an argument value; Printer() can remain no-argument if its body explicitly supplies a serial to super.

Question 4 of 10

What is printed by new Leaf();?

class Root {
    Root() { System.out.print("R"); }
}
class Branch extends Root {
    Branch() { System.out.print("B"); }
}
class Leaf extends Branch {
    Leaf() { this(7); System.out.print("L"); }
    Leaf(int n) { System.out.print(n); }
}
  1. L7BR
  2. RBL7
  3. RB7L
  4. RRB7L
Show answerHide answer
Answer: C
Leaf() delegates to Leaf(int), whose implicit superclass call enters Branch and then Root. Their bodies print "R" and "B", then Leaf(int) prints 7 and the delegating constructor prints "L". Delegation does not construct the root part a second time.

Question 5 of 10

Meter declares public int read(). A subclass writes @Override public int read(int offset). Why does compilation fail?
  1. The return type must change in every subclass override.
  2. The annotation prevents public methods from being inherited.
  3. The parameter list changes, so the method is an overload.
  4. The subclass must redeclare every field before overriding.
Show answerHide answer
Answer: C
The inherited method takes no arguments, while the new declaration takes an integer. That is a different signature, so it overloads read instead of overriding it. @Override catches the mismatch; returning int is compatible with the original method.

Question 6 of 10

What does the final statement print?

class Parcel {
    public String toString() { return "parcel"; }
}
class ChilledParcel extends Parcel {
    @Override public String toString() {
        return super.toString() + " cold";
    }
}
Parcel item = new ChilledParcel();
System.out.println("Send " + item);
  1. "Send parcel"
  2. "Send parcel cold"
  3. "Send ChilledParcel cold"
  4. "Send null cold"
Show answerHide answer
Answer: B
Concatenation obtains the object's text through toString(), and the actual chilled-parcel object runs its override. That method obtains "parcel" from super.toString() and appends " cold". Declaring the reference as Parcel does not suppress the override.

Question 7 of 10

A final Label class has only public boolean equals(Label other), which compares its text, and inherits equals(Object). Two distinct labels have the same text. Both references are stored in variables declared as Object. What does a.equals(b) return?
  1. false, because the selected method still compares identity.
  2. true, because matching text selects the Label overload.
  3. true, because Object compares each field automatically.
  4. Compilation fails because Object has no equality method.
Show answerHide answer
Answer: A
With Object references, the call selects equals(Object), which Label has not overridden. The inherited implementation compares the two distinct objects by identity and returns false. The same-named method taking Label is an overload and is not selected from the runtime argument class.

Question 8 of 10

abstract class Sensor declares public abstract double reading();. What must a concrete LightSensor extends Sensor provide or inherit?
  1. A public constructor named Sensor.
  2. A static double reading() implementation.
  3. A private double reading() implementation.
  4. A public double reading() implementation.
Show answerHide answer
Answer: D
A concrete subclass must complete the inherited abstract operation with a compatible instance method. Its implementation must keep the public access promised by Sensor. A static method is not an instance override, and a private method would reduce the required access.

Question 9 of 10

Why does this class fail to compile?

interface Resettable { void reset(); }
class Counter implements Resettable {
    void reset() { }
}
  1. reset() must be static in Counter.
  2. reset() must be public in Counter.
  3. Resettable needs a constructor body.
  4. Counter must extend Resettable.
Show answerHide answer
Answer: B
The interface's abstract reset() method is implicitly public. The implementation has package-private access, so it weakens the promised access and fails compilation. A class implements an interface; changing the relationship to extends would not fix the method declaration.

Question 10 of 10

What does new TimedJob().cost() return?

class Job {
    public int rate() { return 3; }
    public int cost() { return 4 * rate(); }
}
class TimedJob extends Job {
    @Override public int rate() { return 5; }
    @Override public int cost() { return super.cost() + 2; }
}
  1. 22
  2. 14
  3. 20
  4. 28
Show answerHide answer
Answer: A
The override first enters Job.cost() through super.cost(). Its ordinary rate() call still runs on the timed-job object, so it returns 5; the inherited calculation gives 20 and the outer override adds 2. Using 3 for the inner call would wrongly disable dynamic binding throughout the superclass method.
Set 2: Use contracts and general reference types10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Robot implements Movable. In Movable unit = new Robot();, what is the declared type of unit?
  1. Robot
  2. Movable
  3. Object
  4. Both Movable and Robot
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A TextDocument and an unrelated Image class must both support exportText(). Each already extends a different class. What common type best expresses this capability?
  1. An Exportable class extended by both as a second superclass.
  2. An Image superclass that also contains text documents.
  3. An Exportable interface implemented by both.
  4. An Object variable that directly exposes exportText().
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

Does this code compile, and why?

interface Named { String name(); }
class Entity {
    public String name() { return "entity"; }
}
class Asset extends Entity implements Named { }
  1. Yes; the inherited public method fulfills the interface contract.
  2. No; each implementing class must redeclare every interface method.
  3. No; the superclass must explicitly implement the same interface.
  4. Yes; Java generates an empty body for missing interface methods.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does new Screen().label() return?

interface Tagged {
    default String label() { return "tagged"; }
}
class Panel {
    public String label() { return "panel"; }
}
class Screen extends Panel implements Tagged { }
  1. "tagged"
  2. "paneltagged"
  3. The declarations cause a compile error.
  4. "panel"
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

Fast and Safe are unrelated interfaces, each with default String mode(). A concrete Service implements both and inherits no class implementation of mode(). What must Service do?
  1. Rely on the interface listed first after implements.
  2. Override mode() to resolve the inherited conflict.
  3. Rename both interface methods inside the class body.
  4. Call Service.super.mode() from its constructor.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

Vehicle has move(), and only Bicycle has ringBell(). Bicycle extends Vehicle. What happens to v.ringBell() after Vehicle v = new Bicycle();?
  1. It fails to compile because Vehicle has no ringBell() method.
  2. It compiles and runs the bicycle implementation of ringBell().
  3. It compiles but throws because the object became a Vehicle.
  4. It compiles and runs an empty method inherited from Object.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

interface Percent declares static int cap(int n), and Gauge implements Percent declares no cap method. How should code call the interface helper with 120?
  1. Gauge.cap(120)
  2. new Gauge().cap(120)
  3. Percent.super.cap(120)
  4. Percent.cap(120)
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A method only iterates shapes and calls area(). It must accept either ArrayList<Shape> or LinkedList<Shape>. What parameter type exposes the needed list operations?
  1. ArrayList<Shape>
  2. LinkedList<Shape>
  3. List<Shape>
  4. Object
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What happens when compiling and running new Row().render()?

interface Renderable { String render(); }
abstract class Base implements Renderable {
    public String render() { return "[" + body() + "]"; }
    protected abstract String body();
}
class Row extends Base {
    @Override protected String body() { return "row"; }
}
  1. It fails because Row must redeclare render().
  2. It compiles and returns "[row]".
  3. It fails because body() must be public.
  4. It compiles and returns "[]".
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A report framework has an abstract Report with private title state, a constructor setting that state, and abstract body(). Reports and unrelated chart objects must also be printable. What structure preserves both requirements?
  1. Replace Report with a default-only interface storing each title.
  2. Make charts extend Report even when they are not report objects.
  3. Keep Report and add a Printable interface to the relevant classes.
  4. Make report classes extend both Report and an abstract Printable.
Show answerHide answer

Save this lesson to see this answer.

Set 3: Follow calls, hidden members and checked casts10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Shape s = null; is followed by s instanceof Circle. Circle extends Shape. What is the result of the test?
  1. false
  2. true
  3. A ClassCastException is thrown.
  4. A NullPointerException is thrown.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What happens to the final statement?

class Animal { }
class Fox extends Animal { }
Animal a = new Animal();
Fox f = (Fox) a;
  1. It fails to compile because downcasts are never allowed.
  2. It creates a Fox containing the original Animal state.
  3. It compiles, then throws ClassCastException.
  4. It succeeds and gives f the same plain Animal object.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What does pick(value) return?

static String pick(Object x) { return "object"; }
static String pick(CharSequence x) { return "sequence"; }
// Inside a method of the class containing these overloads:
Object value = "notes";
  1. "sequence"
  2. "notes"
  3. The call throws ClassCastException.
  4. "object"
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What text is produced by p.label + ":" + p.show()?

class Parent {
    String label = "P";
    String show() { return label; }
}
class Child extends Parent {
    String label = "C";
    @Override String show() { return label; }
}
Parent p = new Child();
  1. "P:P"
  2. "P:C"
  3. "C:C"
  4. "C:P"
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A method accepts List<Vehicle> and may add a bus. Bicycle extends Vehicle and Bus extends Vehicle. Why can a List<Bicycle> not be passed to it?
  1. A Bicycle object cannot be assigned to any Vehicle reference.
  2. Lists prevent inheritance whenever their elements are mutable objects.
  3. The method could insert a Bus into a list promised to contain bicycles.
  4. The method would convert every bicycle object into a plain Vehicle.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What does b.test() return?

class Base {
    String use(Object x) { return "BO"; }
    String use(String x) { return "BS"; }
}
class Sub extends Base {
    @Override String use(Object x) { return "SO"; }
    @Override String use(String x) { return "SS"; }
    String test() {
        Object text = "x";
        return super.use(text) + ":" + use(text);
    }
}
Sub b = new Sub();
  1. "BO:SO"
  2. "BS:SS"
  3. "BO:SS"
  4. "SO:SO"
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What does new Sub(); print with standard Java 25?

class Base {
    Base() { System.out.print(value()); }
    int value() { return -1; }
}
class Sub extends Base {
    int n = 8;
    Sub() { super(); n = 11; }
    @Override int value() { return n; }
}
  1. -1
  2. 8
  3. 11
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What is printed by this code? Tool declares no methods.

class Tool { }
class Drill extends Tool {
    int speed() { return 6; }
}
Tool tool = new Drill();
Object copy = tool;
if (copy instanceof Drill d) {
    System.out.print(d.speed());
}
System.out.print(":" + (copy == tool));
  1. "6:false"
  2. "6:true"
  3. "0:true"
  4. The speed call fails to compile.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Creator has public Object create(). TextCreator extends Creator overrides it. What return-type and access combination is valid?
  1. protected String create()
  2. public String create()
  3. public int create()
  4. private Object create()
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A plugin system has many Document subclasses. Its preview method uses instanceof branches to compute each document's word count. New document classes are often added. What change lets new classes support preview without editing that type-test chain?
  1. Cast each Document to Object and keep the same subtype branches.
  2. Move the subtype branches into a static method on the preview class.
  3. Give every subtype a field with the same name and read it through Document.
  4. Declare wordCount() on Document and implement it in each concrete subtype.
Show answerHide answer

Save this lesson to see this answer.

Set 4: Model values, compare them and sort them10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

An application uses enum Status { NEW, SENT, DONE }. What does Status.valueOf("sent") do?
  1. It returns Status.SENT.
  2. It returns the string "sent".
  3. It returns Status.NEW.
  4. It throws IllegalArgumentException.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

For record Reading(int count, String unit) { }, what generated call reads the count from a variable r?
  1. r.getCount()
  2. r.count()
  3. r.count
  4. r.readCount()
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

enum Band { LOW(2), HIGH(9); ... } stores the constructor argument in an immutable code field. A new constant MID(5) is inserted between LOW and HIGH. How do HIGH's ordinal() and stored code change?
  1. Its ordinal becomes 2; its code stays 9.
  2. Its ordinal stays 1; its code becomes 2.
  3. Its ordinal becomes 2; its code becomes 2.
  4. Its ordinal stays 1; its code stays 9.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Two unequal Token objects return the same hash code. Their equals(Object) implementations correctly compare the fields defining each token. Does the shared hash code violate the equality and hashing contract?
  1. Yes; every object must have a unique hash code.
  2. Yes; sharing a hash code makes the tokens equal.
  3. No; unequal objects are allowed to share a hash code.
  4. No; hash collections ignore equality after hashing.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

record Result(int seconds) implements Comparable<Result> returns Integer.compare(seconds, other.seconds). What sign does new Result(418).compareTo(new Result(405)) have?
  1. Negative.
  2. Positive.
  3. Zero.
  4. It has no result because records cannot implement interfaces.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What does the final statement print?

record Tag(String text) {
    Tag { text = text.trim(); }
}
Tag a = new Tag(" north ");
Tag b = new Tag("north");
System.out.println(a.equals(b));
  1. true
  2. false
  3. "north"
  4. Compilation fails because parameters cannot be reassigned.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A base Ticket.equals accepts any Ticket with the same code. PriorityTicket extends Ticket accepts only PriorityTicket objects and also compares priority. A plain ticket t and a priority ticket p have the same code. What do t.equals(p) and p.equals(t) return, and does that respect symmetry?
  1. Both return false; the pair respects symmetry.
  2. Both return true; the pair respects symmetry.
  3. They return true and false; the pair violates symmetry.
  4. They return false and true; the pair violates symmetry.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What is printed? List and ArrayList are imported.

record Batch(List<String> names) { }
var original = new ArrayList<String>(List.of("Rin"));
Batch first = new Batch(original);
Batch second = new Batch(new ArrayList<>(original));
original.add("Sol");
System.out.println(first.equals(second) + ":"
    + first.names().size() + ":" + second.names().size());
  1. "true:2:1"
  2. "false:1:1"
  3. "true:1:1"
  4. "false:2:1"
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What happens when Collections.sort(list) runs? The needed java.util types are imported.

record Rank(int score) implements Comparable<Rank> {
    public int compareTo(Rank other) {
        return Integer.compare(score, other.score);
    }
}
List<Rank> list = List.of(new Rank(8), new Rank(3));
  1. It sorts the list into scores 3, then 8.
  2. It fails to compile because Rank has no comparison method.
  3. It throws UnsupportedOperationException.
  4. It leaves scores 8, then 3 because record order is fixed.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What does Rule.DOUBLE.apply(Rule.NEXT.apply(4)) return?

enum Rule {
    NEXT {
        int apply(int n) {
            return switch (n) { case 4 -> 6; default -> n + 1; };
        }
    },
    DOUBLE { int apply(int n) { return 2 * n; } };
    abstract int apply(int n);
}
  1. 12
  2. 10
  3. 9
  4. 16
Show answerHide answer

Save this lesson to see this answer.

Set 5: Combine Java rules and diagnose failures10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Which Java type suits a fixed set of export choices named PDF, TEXT and HTML?
  1. A record with three mutable choice fields.
  2. An enum declaring those three constants.
  3. An integer whose valid values are undocumented.
  4. A superclass with one subclass for every file.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does new Terminal().name() return?

interface Basic {
    default String name() { return "basic"; }
}
interface Detailed extends Basic {
    default String name() { return "detail"; }
}
class Terminal implements Basic, Detailed { }
  1. "basic"
  2. "basicdetail"
  3. "detail"
  4. The class has an unresolved default conflict.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A List<Notifier> contains objects of several unrelated classes implementing Notifier.send(). A loop calls n.send() on every element. A new PushNotifier implements the same contract correctly. What change is needed in the loop to support its instances?
  1. No change; the existing interface call dispatches to each implementation.
  2. Add a cast to PushNotifier before every call to send().
  3. Replace the interface variable with an Object variable for each element.
  4. Add a branch so all older objects call the new class implementation.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What is printed with standard Java 25?

class Base {
    Base(int n) { System.out.print("B" + n); }
}
class Sub extends Base {
    Sub(int n) {
        if (n < 0) throw new IllegalArgumentException();
        super(n + 1);
        System.out.print("S" + n);
    }
}
new Sub(2);
  1. "S2B3"
  2. "B2S2"
  3. Compilation fails because super is not first.
  4. "B3S2"
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A mutable Code class defines equality and hashing using its text field. A HashSet<Code> contains c with text "red". The program must change its text to "blue", keep the same object, and preserve reliable set lookup. In what order should it update the object and set?
  1. Change the text, remove the object, then add it again.
  2. Add the object again, change its text, then remove it.
  3. Remove the object, add it again, then change its text.
  4. Remove the object, change its text, then add it again.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What does x.call(arg) + ":" + x.call("x") produce?

class Alpha {
    String call(Object x) { return "AO"; }
    String call(String x) { return "AS"; }
}
class Beta extends Alpha {
    @Override String call(Object x) { return "BO"; }
    @Override String call(String x) { return "BS"; }
}
Alpha x = new Beta();
Object arg = "data";
  1. "AO:AS"
  2. "BO:BS"
  3. "BO:AS"
  4. "BS:BS"
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What does the final statement print? List and ArrayList are imported.

record Group(List<String> names) {
    Group { names = List.copyOf(names); }
}
var original = new ArrayList<String>();
original.add("Uma");
Group group = new Group(original);
original.add("Zed");
System.out.println(group.names().size() + ":" + original.size());
  1. "1:2"
  2. "2:2"
  3. "1:1"
  4. The record constructor fails to compile.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What happens when this code is compiled?

interface Ready {
    default String state() { return "ready"; }
}
abstract class Base {
    public abstract String state();
}
class Console extends Base implements Ready { }
  1. Compilation succeeds; Console inherits the Ready default implementation.
  2. Compilation succeeds; the Base method returns null until overridden.
  3. Compilation fails; Console must implement the inherited abstract method.
  4. Compilation fails; abstract classes cannot be combined with interfaces.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Two record Player(String name, int points) objects have different names but the same points. Their custom compareTo compares only points using Integer.compare. What do equals and compareTo report for these two objects?
  1. equals is true; compareTo is zero.
  2. equals is false; compareTo must be nonzero.
  3. equals is true; compareTo must be nonzero.
  4. equals is false; compareTo is zero.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A library already exposes interface Storable { String saveText(); }. Many new implementations share a private path field, constructor validation, and helper code, while some existing implementations extend other classes. How can the library offer reuse while preserving the common API?
  1. Add an abstract Storable implementation that classes may extend.
  2. Replace Storable with a class that every implementation must extend.
  3. Move the private instance path field into the Storable interface.
  4. Add a second superclass to the existing implementations for shared storage.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

72 Key Terms, each with its definition

A

Abstract class
An abstract class is a class that cannot be instantiated directly and can combine shared fields and code with unfinished operations.
An abstract class Shape can store a name while requiring concrete circles and rectangles to supply an area.
Abstract method
An abstract method declares a required operation without a body, leaving concrete subclasses to supply or inherit its implementation.
The abstract method area() prevents an unfinished concrete shape from silently using a zero-area placeholder.
Accessor
An accessor is a method returning a stored value; a generated record accessor uses its component's exact name without a getter prefix.
The accessor for a count record component is count(), so getCount() is unavailable unless explicitly declared.
Actual type
The actual type is the class of the object a reference points to, fixed when that object is created and used when selecting instance overrides.
A Person reference can point to a student whose actual type remains Student after the upcast.

C

Canonical constructor
A canonical constructor is a record constructor whose parameters match the component list and initialize the record's component fields.
The canonical constructor of Point(int x, int y) accepts the x coordinate followed by the y coordinate.
Capability
A capability is an operation or set of operations that an object promises to support, often expressed by an interface across unrelated classes.
Exporting is a capability that a text label and a circle can both provide while keeping different superclasses.
ClassCastException
A ClassCastException is a runtime failure caused by a reference cast whose non-null object is incompatible with the requested target type.
A ClassCastException occurs when a plain Animal object is cast to its more specific Fox subclass.
Compact constructor
A compact constructor is a record constructor body written without repeating its parameters, with automatic component assignment afterward.
A compact constructor can trim an incoming name before that normalized value is assigned to the final component field.
Comparable
Comparable is the Java interface through which a type defines natural ordering by comparing an instance with another using compareTo.
A runner implements Comparable<Runner> so a sorting method can arrange runners by finishing time.
Composition
Composition models a has-a relationship by storing references to other objects in fields instead of claiming to be those objects.
Composition lets a Customer hold an Address without making every customer usable as an address.
Concrete class
A concrete class is a non-abstract class that supplies or inherits implementations of all required abstract methods and can have instances.
Rectangle is a concrete class once it completes the area operation required by its abstract shape base.
Concrete method
A concrete method has an implementation body, so callers can execute its code instead of relying on a subclass to complete a declaration.
The concrete method Shape.describe() can reuse the name and call each subclass's implementation of area().
Constant-specific body
A constant-specific body is an enum constant's own class body, used to provide behavior particular to that named value of the enum.
A constant-specific body lets MULTIPLY implement an operation differently from the enum's ADD constant.
Constructor chain
A constructor chain is a sequence of same-class and superclass constructor invocations that sets up the parts of one new object.
A constructor chain for a leaf class reaches its base constructors before the remaining leaf body runs.
Covariant return type
A covariant return type is a subtype used as an override's reference return type in place of the original, more general return type.
A covariant return type allows an override to return String when the superclass method returns Object.

D

Declared type
The declared type is the static type assigned to a variable in the source code, determining which operations the compiler permits through it.
In Person p = new Student(...), the declared type is Person, so student-only methods are not directly available.
Default constructor
A default constructor is a no-argument constructor Java supplies when a class declares none, with a call to super() in its body.
Adding Person(String name) removes the default constructor that a subclass may have relied on.
Default method
A default method is a public interface instance method with a body that implementing classes inherit and may replace with an override.
A default method needsCharge() can use batteryPercent() without knowing any device's private field layout.
Default-method conflict
A default-method conflict arises when unrelated interfaces supply competing inherited defaults that the implementing class must resolve.
A default-method conflict requires SmartLamp to override the matching descriptions from Lamp and Speaker.
Downcasting
Downcasting treats a reference as a more specific type after a runtime compatibility check, without converting or replacing its object.
Downcasting a person reference to a student works only when the referenced object already has the student type.
Dynamic binding
Dynamic binding selects an overridden instance-method implementation at run time using the actual class of the receiver object.
Dynamic binding lets an inherited hello() that calls who() reach B.who() when the receiver belongs to class B.

E

Enum
An enum is a Java type defining a fixed set of named constants, preventing ordinary callers from substituting arbitrary strings or numbers.
An enum for delivery states can restrict an order to the named values NEW, SENT and DONE.
Enum constant
An enum constant is one named value declared in an enum, with a single instance that may hold data and provide constant-specific behavior.
The enum constant CupSize.LARGE can store a capacity of 500 mL and be compared with identity equality.
Equality contract
The equality contract requires reflexive, symmetric, transitive and consistent comparisons, with every non-null object unequal to null.
The equality contract rules out a book accepting an ebook when the ebook rejects the same book in return.

F

Field hiding
Field hiding occurs when a subclass declares a field with the same name as an inherited field, leaving separate fields selected by declared type.
Field hiding can make p.label read the parent field while p.show() runs a child override using a different label.
Field initializer
A field initializer is an expression in a field declaration that assigns its initial value when that field's initialization stage executes.
An instance field initializer in a subclass runs after that object's superclass constructor returns.
Final method
A final method is a method whose implementation cannot be overridden by a subclass, preserving that implementation in descendants.
A final method for calculating a checked value prevents a subclass from replacing that calculation.
Flexible constructor body
A flexible constructor body permits restricted statements before an explicit constructor invocation, a standard feature in Java 25.
A flexible constructor body can reject a negative parameter before passing it to the superclass constructor.

H

Hash code
A hash code is an integer used to locate candidates in a hash collection; objects comparing equal must return the same hash code.
A book's hash code can be built from its catalog code and edition when those fields also define equality.
Hash collision
A hash collision occurs when unequal objects return the same hash code, which is permitted because equality can distinguish them.
A hash collision between two different tokens does not require their equality method to return true.

I

Identity
Identity means that references point to the very same object, regardless of whether separate objects happen to contain equal values.
Two separately constructed books can have equal codes while their distinct identity makes a == b false.
Implementation
An implementation is working code that fulfills a method contract, supplied by a class or inherited through a suitable method declaration.
A public implementation inherited from Entity can fulfill Named.name() for an Asset subclass.
Inheritance
Inheritance is a relationship in which a subclass builds on a superclass and supports the operations available through that superclass.
Inheritance lets a Student reuse Person.getName() while adding its own credits and study behavior.
instanceof
The instanceof operator tests whether a reference denotes an instance of a given type or subtype, returning false when the reference is null.
An instanceof test lets code check whether a person has the student type before using a student-only method.
Integer overflow
Integer overflow occurs when an integer calculation exceeds the representable range, potentially making subtraction report an incorrect ordering sign.
Integer overflow can break a comparison based on subtraction; Integer.compare avoids that comparison error.
Interface
An interface is a named type contract that allows classes to provide a common capability without sharing one stateful superclass.
The Chargeable interface lets a phone and a scooter support the same calls while charging at different rates.
Interface constant
An interface constant is a field implicitly declared public, static and final, belonging to the interface rather than to individual objects.
The interface constant Chargeable.FULL can hold 100 while each device stores its own changing battery field.
Interface type
An interface type is a type used for references, parameters or results to expose a capability while allowing different implementing classes.
Using List as an interface type lets one method accept both array-backed lists and linked lists.

M

Method hiding
Method hiding occurs when a subclass declares a static method with the same signature as a static method available from its superclass.
Method hiding makes the compile-time type select a static method even when a reference holds a subclass object.
Method signature
A method signature identifies a method by its name and parameter types for overload selection and overriding; return type alone is not enough.
Changing a parameter from Object to String changes the method signature and creates an overload.
More specific interface
A more specific interface extends another interface and takes priority when both provide a matching default for an implementing class.
A more specific interface Detailed supplies the chosen name() when it extends Basic and replaces its default.

N

Natural order
A natural order is the default ordering defined by a type's comparison method, used by sorting operations that request no alternative ordering.
A runner's natural order can place smaller finishing times before larger ones when a list is sorted.

O

Object
Object is the root class of Java's class hierarchy, providing common methods inherited by ordinary classes directly or indirectly.
An Object variable can hold a student or an array, and both support the methods every object has.
Ordering tie
An ordering tie occurs when compareTo returns zero, meaning two objects occupy the same comparison rank under the chosen ordering rule.
An ordering tie by race time can occur for runners with different names, even when their records are unequal.
Ordinal
An ordinal is an enum constant's zero-based declaration position, which can change when constants are inserted before that value.
In an enum declared as LOW, HIGH, the ordinal of HIGH changes from 1 to 2 when MID is inserted before it.
Overloading
Overloading declares methods with the same name but different parameter lists, letting the compiler choose an applicable signature.
Overloading can add print(String text) beside print(int number) without replacing either method.
Override annotation
The Override annotation, written @Override, asks the compiler to verify that a method overrides or implements an inherited method.
The Override annotation catches tostring() when the programmer intended to replace toString().
Overriding
Overriding supplies a subclass implementation of an inherited instance method with the same name and matching parameter types.
Overriding yearlyPay() lets a manager add a bonus while callers keep using the employee operation.

P

Partial implementation
A partial implementation supplies some interface behavior in an abstract class while leaving concrete subclasses to finish the required operations.
A partial implementation of Storable can share path validation while concrete classes supply saved text.
Pattern variable
A pattern variable is a typed local variable introduced by a successful type pattern, available where that matching result is known to hold.
The pattern variable student in person instanceof Student student safely exposes the matched object's ID method.
Polymorphism
Polymorphism lets a general reference work with different subtypes through shared operations, with each object supplying its own behavior.
Polymorphism lets one loop call area() on circles, rectangles and triangles without separate type branches.
Private interface method
A private interface method is a helper with a body visible only to code inside that interface, allowing its working methods to share code.
A private interface method can remove duplicated checks from two default methods without becoming part of the public contract.
Protected access
Protected access permits access within a package and restricted subclass access across packages, without making a member public.
Protected access lets a subclass use a superclass helper while keeping that helper out of the public API.

R

Receiver
A receiver is the object on which an instance method is invoked, whose actual class guides dynamic dispatch of the selected method signature.
In shape.area(), the receiver is the shape object, even when the variable has an abstract declared type.
Record
A record is a concise Java class for a fixed group of component values, with generated construction, access, equality, hashing and text behavior.
A record Point(int x, int y) groups two coordinates and supplies component-based equality without hand-written methods.
Record component
A record component is a value declared in a record header, determining a private final field, constructor parameter and public accessor.
The record component seconds can be read from a runner using the generated method runner.seconds().
Runtime class
The runtime class is the actual class of an object, reported by getClass() independently of the declared type of its reference.
The runtime class of a student remains Student even when a method receives it through a Person parameter.

S

Shallow immutability
Shallow immutability fixes an object's own field references or values while allowing mutable objects reached through those references to change.
Shallow immutability lets a record keep the same list reference even when another reference adds a name to that list.
Shared state
Shared state in a superclass is the common per-object data that related subclasses obtain as part of each object's base-class portion.
A shape's name and position are shared state when one abstract superclass defines them for every shape.
Single inheritance
Single inheritance means a Java class has at most one direct superclass, although it may belong to a longer chain of subclasses.
Single inheritance prevents a class from extending both Student and Teacher as direct superclasses.
Static interface method
A static interface method is a method belonging to an interface itself, called through its name and not inherited by implementing classes.
The static interface method Chargeable.clamp(130) can return 100 without creating a device object.
Subclass
A subclass is a class declared to extend another class, adding state or behavior while remaining usable as that more general type.
A GraduateStudent subclass can build on Student and still be used wherever a Person is expected.
Superclass
A superclass is a class whose accessible members a subclass inherits, forming the more general type in their class relationship.
Person is the superclass of Student, so a method accepting a person can also receive a student.
Superclass constructor invocation
A superclass constructor invocation uses super(...) to pass arguments to a direct superclass constructor for the current object.
A superclass constructor invocation passes a student's name to Person, which owns the private name field.
Superinterface
A superinterface is an interface extended by another interface, whose required operations become part of the extending contract.
Chargeable is a superinterface of SmartDevice when the latter extends it along with Connectable.
Switch expression
A switch expression chooses a result from matching cases and must cover the permitted inputs, with enum constants supporting concise cases.
A switch expression listing all three constants of a size enum can produce "S", "M" or "L" without a default branch.
Symmetry
Symmetry in equality requires that reversing two objects in an equality comparison gives the same result for both directions.
Symmetry fails if book.equals(ebook) is true but ebook.equals(book) is false for those same objects.

T

Text representation
A text representation is the string returned for an object by toString(), used when the object is printed or joined to other text.
A student's text representation can show a name and credits in place of the default class-and-hash text.
Transitivity
Transitivity in equality requires an object equal to a second and that second equal to a third to also be equal to the third.
Transitivity makes three books with the same equality-defining code and edition agree in pairwise comparisons.

U

Unmodifiable copy
An unmodifiable copy preserves a collection's contents against later structural edits through the original or returned collection reference.
An unmodifiable copy from List.copyOf protects a team's membership list from additions to the caller's mutable list.
Upcasting
Upcasting treats a reference as a superclass or implemented interface type, safely widening its view without changing the object itself.
Upcasting a phone to Chargeable keeps its battery data but exposes only the operations available through that type.

V

Value equality
Value equality treats objects as equal when their selected data agrees, even if the references point to distinct object instances.
Value equality can make two separately created books equal when their catalog code and edition match.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started