UnsupportedOperationException and LSP violation
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Throwing UnsupportedOperationException is not automatically an LSP violation. The decision depends on the contract of the base type. If CustomerRepository.save is guaranteed for every repository, a read-only subtype that rejects save cannot replace the base type for its clients; it has weakened a promised capability.
Java's List is different. Its documentation marks several mutating methods as optional and explicitly allows UnsupportedOperationException. An unmodifiable list therefore follows the published List contract, even though code that casually assumes all lists are mutable may still fail.
When clients truly require a capability, clearer types are usually safer. A reader can implement CustomerReader, while writable implementations also implement CustomerWriter. LSP is about honoring observable guarantees, not banning one exception class.
UnsupportedOperationException and LSP violation
Direct Answer
UnsupportedOperationException indicates an LSP problem when the parent contract promises that the operation is supported but a subtype rejects it. It is not automatically a violation: Java APIs such as List explicitly document optional operations, so callers must not assume every List is mutable. A clearer capability-specific interface is often better when callers need that guarantee.
Code Example
interface CustomerReader {
Customer findById(long id);
}
interface CustomerWriter {
void save(Customer customer);
}
final class ReadOnlyCustomers implements CustomerReader {
@Override
public Customer findById(long id) {
// read from a replica
return null;
}
}
// A caller that requires saving asks for CustomerWriter.
// ReadOnlyCustomers never claims to provide that capability.
By contrast, List.add is documented as optional. An unmodifiable List may reject it without breaking that published contract; the caller must choose a mutable type or make a copy when mutation is required.
Common Mistakes & Pitfalls
- Calling every UnsupportedOperationException an LSP violation: first check whether the operation is guaranteed or documented as optional.
- Hiding a promised capability: a subtype must not reject behavior its base contract requires.
- Relying on a broad type when capability matters: use a focused contract for read-only versus writable behavior.
- Treating successful compilation as proof of substitutability: behavior and documented guarantees matter too.
Check your understanding
Answer these without looking back at the article.
- Is Java Collections unmodifiableList an LSP violation?
- How do you design interfaces to avoid this problem?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.