June 17, 2026 in code-smells4 minutes
A intermediate-level guide to Refused Bequest: before-and-after java code and diagrams for a CS student.
Imagine you inherit a massive estate from a distant relative. Along with the house and land, you are legally handed a vintage car you have no interest in driving and a complex manufacturing business you have no desire to run. Even if you never touch the car or visit the factory, they are now part of your legal responsibility simply because you are the heir.
In software, inheritance can become this same kind of burden. When we use extends to gain access to some useful methods, we don’t just inherit the good parts; we inherit everything else too. If a subclass receives a method it cannot logically implement, it is forced to “refuse” that behavior.
class Device {
public void powerOn() {}
public void operate() {}
public void recharge() { System.out.println("Recharging battery..."); }
}
class ManualClock extends Device {
@Override
public void recharge() {
// Refused Bequest: Clocks don't need recharging
throw new UnsupportedOperationException();
}
@Override
public void operate() {
System.out.println("Winding the clock manually.");
}
}
classDiagram
Device <|-- ManualClock
class Device {
+powerOn()
+operate()
+recharge()
}
class ManualClock {
+recharge()
+operate()
}
Refused Bequest is a code smell that occurs when a subclass inherits methods from its parent but has no interest or ability to perform the actions those methods describe.
You can spot this in two ways:
{} because the behavior is irrelevant to the subclass.UnsupportedOperationException. This is a loud, runtime way of saying, “I know I’m supposed to do this, but I refuse.”While it might seem tempting to use inheritance just as a shortcut to grab some variables or helper methods from a parent class, this creates a fragile hierarchy. It violates the principle that a subclass should be a specialized version of its parent that can safely stand in for that parent anywhere in the code. If a piece of code expects a Device and calls recharge(), but receives a ManualClock that throws an exception, the program crashes. This break in the “behavioral contract” is the core problem.
sequenceDiagram
participant Client
participant MC as ManualClock
Client ->> MC: recharge()
MC -->> Client: throw UnsupportedOperationException
To fix a Refused Bequest, we must stop trying to force an “is-a” relationship where it doesn’t fit. Instead of forcing every device into one giant hierarchy, we should use the Replace Inheritance with Composition approach or split responsibilities using interfaces. This follows the Interface Segregation Principle: clients should only be forced to depend on methods they actually use.
interface Operatable {
void operate();
}
interface Rechargeable {
void recharge();
}
class Device implements Operatable {
@Override
public void operate() { System.out.println("Operating device..."); }
}
class BatteryPoweredDevice extends Device implements Rechargeable {
@Override
public void recharge() { System.out.println("Recharging battery..."); }
}
class ManualClock implements Operatable {
@Override
public void operate() { System.out.println("Winding the clock manually."); }
}
classDiagram
Operatable <|.. Device
Rechargeable <|.. BatteryPoweredDevice
Device ..|> Operatable
BatteryPoweredDevice --|> Device
ManualClock ..|> Operatable
class Operatable {
+operate()
}
class Rechargeable {
+recharge()
}
class Device {
+operate()
}
class BatteryPoweredDevice {
+operate()
+recharge()
}
class ManualClock {
+operate()
}
By moving the specialized behaviors into distinct interfaces like Operatable and Rechargeable, we allow classes to pick and choose exactly what they are capable of doing. A ManualClock is now just an Operatable object; it no longer carries the heavy, unnecessary baggage of a charging requirement.
Refactoring toward composition and smaller interfaces is almost always the right move when your hierarchy feels “bloated.” If you see subclasses throwing exceptions for parent methods, you have already passed the point where simple inheritance is sufficient.
However, don’t over-engineer everything into tiny fragments immediately. Inheritance is still highly effective when there is a strict, undeniable “is-a” relationship (e.g., a Dog is an Animal). If the hierarchy is shallow and every subclass truly needs every method of the parent, adding extra layers of interfaces might add unnecessary complexity to your system.
UnsupportedOperationException in an overridden method is a major red flag for Refused Bequest.Polymorphic use of rechargeable behavior
Device d = new BatteryPoweredDevice();
d.recharge(); // Works as expectedThe danger of the Refused Bequest
// This would cause a runtime error in the 'before' version
Device clock = new ManualClock();
try {
clock.recharge();
} catch (UnsupportedOperationException e) {
System.out.println("Error: Clock cannot be recharged!");
}