Ответ
Принцип подстановки Лисков (LSP): Объекты базового класса должны быть заменяемы объектами производного класса без изменения корректности программы.
Классическое нарушение в Java: Класс java.util.Stack<E>, который наследуется от java.util.Vector<E>.
Почему это нарушение LSP?
Stack по своей семантике — структура данных LIFO (Last-In-First-Out). Его публичный контракт — операции push, pop, peek. Наследуясь от Vector, он «наследует» множество публичных методов, которые нарушают этот контракт и позволяют модифицировать стек произвольным образом.
import java.util.Stack;
public class LSPViolationExample {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("First"); // Корректная операция стека
stack.push("Second");
// НАРУШЕНИЕ LSP: Методы, унаследованные от Vector,
// ломают инварианты стека (доступ к произвольному элементу).
stack.insertElementAt("Intruder", 0); // Метод из Vector!
stack.remove(1); // Метод из Vector!
// Теперь поведение pop() не соответствует LIFO.
System.out.println(stack.pop()); // Что будет выведено? Неочевидно.
}
}
Следствия нарушения:
- Ненадежность: Клиентский код, использующий
StackкакVector, может сломать логику стека. - Нарушение инкапсуляции: Внутреннее состояние (порядок элементов) можно изменить в обход основных методов.
- Невозможность безопасной замены:
Stackне может быть использован везде, где ожидаетсяVector, без риска ошибок.
Правильная реализация (соответствующая LSP) должна была использовать композицию: хранить Vector или ArrayList как приватное поле и предоставлять только методы стека, делегируя им вызовы к внутренней коллекции.