Refused Bequest

Keywords: refused bequest, code ai

Refused Bequest is a code smell where a subclass inherits from a parent class but ignores, overrides without use, or throws exceptions for the majority of the inherited interface — indicating a broken inheritance relationship that violates the Liskov Substitution Principle (LSP), meaning objects of the subclass cannot safely be substituted wherever the parent is expected, which defeats the entire purpose of the inheritance relationship and creates brittle, misleading type hierarchies.

What Is Refused Bequest?

The smell manifests when a subclass rejects its inheritance:

- Exception Throwing: ReadOnlyList extends List overrides add() and remove() to throw UnsupportedOperationException — declaring "I am a List" but refusing to behave as one.
- Empty Method Bodies: Subclass overrides parent methods with empty implementations — pretending to support the interface while silently doing nothing.
- Selective Inheritance: A Square extends Rectangle where setting width and height independently (valid for Rectangle) produces invalid states for Square — inheriting an interface the subclass cannot correctly implement.
- Constant Overriding: Subclass inherits 15 methods but meaningfully uses 2, overriding the other 13 with stubs.

Why Refused Bequest Matters

- Liskov Substitution Principle Violation: LSP states that code using a base class reference must work correctly with any subclass. When ReadOnlyList throws on add(), any code that accepts a List and calls add() will unexpectedly fail at runtime — a type system contract is broken. This is the most dangerous aspect: the breakage is discovered at runtime, not compile time.
- Polymorphism Corruption: Inheritance's value lies in polymorphic behavior — treat all subclasses uniformly through the parent interface. A refusing subclass forces callers to type-check before each operation (if (list instanceof ReadOnlyList)) — collapsing polymorphism into manual dispatch and spreading awareness of subtype internals throughout the codebase.
- Test Unreliability: Test suites written against the parent class interface will fail for refusing subclasses. If automated tests call all inherited methods against all subclasses (a standard practice), refusing subclasses generate spurious test failures that mask real problems.
- Documentation Lies: The class hierarchy is a form of documentation — ReadOnlyList extends List tells every reader "ReadOnlyList is-a fully functional List." When this is false, the hierarchy actively misleads developers about behavior.
- API Design Failure: In widely used libraries, Refused Bequest in public APIs forces all users to handle unexpected exceptions from operations they had every right to call — a usability and reliability failure that affects entire ecosystems.

Root Causes

Accidental Hierarchy: The subclass was placed in the hierarchy for code reuse, not because there is a genuine is-a relationship. Square extends Rectangle was done to reuse rectangle methods, not because squares are fully substitutable rectangles.

Evolutionary Hierarchy: The parent's interface expanded over time. The subclass was created when the parent had 5 methods; now it has 20, and 15 are not applicable to the subclass.

Legacy Constraint: The hierarchy was inherited from an older design that made sense in a different context.

Refactoring Approaches

Composition over Inheritance (Most Recommended):
``
// Before: Bad inheritance
class ReadOnlyList extends ArrayList {
public boolean add(E e) { throw new UnsupportedOperationException(); }
}

// After: Composition — use the list, do not claim to be one
class ReadOnlyList {
private final List<E> delegate;
public E get(int i) { return delegate.get(i); }
public int size() { return delegate.size(); }
// Only expose what ReadOnlyList actually supports
}
`

Extract Superclass / Pull Up Interface: Create a narrower shared interface that both classes can fully implement. ReadableList (with get, size, iterator) as the shared interface, with MutableList and ReadOnlyList as separate, non-related implementations.

Replace Inheritance with Delegation: The subclass keeps a reference to a parent-type object and delegates only the methods it wants to support, rather than inheriting the entire interface.

Tools

- SonarQube: Detects Refused Bequest through analysis of overridden methods that throw UnsupportedOperationException` or have empty bodies.
- Checkstyle / PMD: Rules for detecting methods that only throw exceptions.
- IntelliJ IDEA: Inspections flag method overrides that always throw — a strong signal of Refused Bequest.
- Designite: Design smell detection including inheritance-related smells for Java and C#.

Refused Bequest is bad inheritance made visible — the code smell that exposes when a class hierarchy has been assembled for code reuse convenience rather than genuine behavioral substitutability, creating a type system that promises behavior it cannot deliver and forcing runtime defenses against what should be compile-time guarantees.

Want to learn more?

Search 13,225+ semiconductor and AI topics or chat with our AI assistant.

Search Topics Chat with CFSGPT