What is the difference between a HAS-A and an IS-A relationship in Java, and how does each show up in code?
answer
- IS-A = extends/implements; HAS-A = field
- IS-A must satisfy Liskov substitutability
- HAS-A = containment + delegation
- Favor composition (HAS-A) over inheritance (IS-A)
- Inheritance for reuse only = code smell
basics
~20 sIS-A means one type is a kind of another (a Car IS-A Vehicle) and uses inheritance (extends). HAS-A means an object holds another as a part (a Car HAS-A Engine) and uses a field. IS-A is inheritance; HAS-A is composition.
solid answer
~40 sIS-A is the inheritance relationship: a subclass is a specialized kind of its superclass, so Dog extends Animal means a Dog IS-AN Animal and can be used wherever an Animal is expected. In Java you express it with extends (for classes) or implements (for interfaces). HAS-A is the containment relationship: one object holds a reference to another as a member field, like a Car HAS-A Engine. You express HAS-A simply by declaring the other type as a field. The practical guidance is to use IS-A only when the subtype genuinely substitutes for the supertype (Liskov); otherwise model the relationship as HAS-A. Most real-world 'part of' or 'uses a' relationships are HAS-A, which is why composition is usually the safer default over inheritance.
code
java · 10 lines// IS-A: SportsCar substitutes for Car
class Car { void drive() {} }
class SportsCar extends Car { void turbo() {} }
// HAS-A: Car holds an Engine as a part
class Engine { void start() {} }
class Car2 {
private final Engine engine = new Engine(); // HAS-A
void start() { engine.start(); } // delegation
}go deeper
Can state IS-A = extends/implements, HAS-A = a field, and give one example of each (Dog IS-A Animal; Car HAS-A Engine).
Explains delegation as the HAS-A mechanism and recognizes that inheritance forces substitutability, so HAS-A is the default for 'uses/part-of' relationships.
Ties IS-A to the Liskov Substitution Principle, names the fragile-base-class coupling that makes inheritance risky, and applies 'favor composition over inheritance' as a deliberate design choice.
Frames the choice in terms of API surface, long-term substitutability guarantees, and evolution cost; can articulate when an inheritance hierarchy is still the right call (stable IS-A, framework template methods) versus when composition keeps the design flexible.
## The two ways objects relate When you design with objects, two objects can relate in two fundamentally different ways. Naming them with plain-English phrases makes the distinction obvious. ### IS-A (inheritance) **IS-A** means one type *is a kind of* another. A `Dog` IS-AN `Animal`; a `SavingsAccount` IS-AN `Account`. In Java you create an IS-A relationship with the **`extends`** keyword (class inherits from a class) or **`implements`** (class promises an interface's behavior). Inheritance means the subclass automatically gets the superclass's fields and methods, and — crucially — an instance of the subclass can be used **anywhere** the superclass type is expected. That substitutability is the whole point of IS-A. The formal rule is the **Liskov Substitution Principle (LSP)**: if `S` IS-A `T`, then you must be able to replace a `T` with an `S` without breaking the program. ```java class Animal { void breathe() {} } class Dog extends Animal { void bark() {} } // Dog IS-A Animal Animal a = new Dog(); // legal: a Dog substitutes for an Animal ``` ### HAS-A (composition / association) **HAS-A** means one object *holds* or *contains* another as a part. A `Car` HAS-AN `Engine`; an `Order` HAS-A list of `LineItem`s. In Java you express HAS-A by declaring the other type as a **member field** (a variable inside the class): ```java class Engine { void start() {} } class Car { private final Engine engine = new Engine(); // Car HAS-A Engine void start() { engine.start(); } // the Car delegates to its part } ``` Here `Car` does not *become* an `Engine`; it simply *uses* one. Calling the Car's method that forwards work to the held object is called **delegation**. ### Why the distinction matters Inheritance is the strongest coupling Java offers: the subclass depends on the superclass's internal details and breaks if the parent changes (the 'fragile base class' problem). It also forces an IS-A claim that must hold *forever*. HAS-A is looser: the container only depends on the contained type's public methods and can swap implementations freely. The classic design guidance — **'favor composition over inheritance'** (from *Effective Java* and *Design Patterns*) — says: reach for HAS-A by default, and use IS-A only when a true, permanent substitutability relationship exists. ### A quick test Ask: 'Can I sensibly say *X is a Y*, and could I pass an X to every method that wants a Y?' If yes, IS-A may fit. If you instead find yourself saying *X has a Y* or *X uses a Y*, it's HAS-A — model it as a field. A common mistake is using inheritance just to reuse code (e.g., `Stack extends Vector`), which leaks the parent's API and violates IS-A; a field plus delegation would have been cleaner.
- Why is 'using inheritance only to reuse code' considered a design smell?Inheritance forces an IS-A claim and exposes the parent's whole API, coupling the subclass to the parent's internals (fragile base class). If you only wanted to reuse the code, a field plus delegation (HAS-A) reuses behavior without the substitutability promise or the leaked API.
- Can a class have both an IS-A and a HAS-A relationship at once?Yes. A class commonly extends one superclass (IS-A) while also holding other objects as fields (HAS-A). For example, a SportsCar extends Car (IS-A) and still has an Engine field (HAS-A).
IS-A is 'a sparrow is a bird' — it belongs to the category. HAS-A is 'a bird has a wing' — the wing is a part, not the category.
saying these in an interview costs you the question
- Saying HAS-A uses 'extends' — HAS-A is a field, not inheritance.
- Claiming any code reuse justifies inheritance — reuse alone is not an IS-A relationship.
- Confusing 'implements an interface' with HAS-A — implementing is still IS-A (multiple inheritance of type).