Нужно ли использовать рекорды для описания нод в Java
Что проверяют
Знаете ли вы ограничения записей: неявный final, финальные компоненты, отсутствие наследования. И умеете ли по характеру узла понять, подойдёт запись или нужен обычный класс.
Как отвечать
Запись это прозрачный носитель данных: компоненты финальные, канонический конструктор и аксессоры по именам компонентов, а также equals, hashCode и toString из коробки. Наследовать класс запись не может, зато реализует интерфейсы, поэтому отлично ложится на запечатанную иерархию и сопоставление с образцом в switch.
Для неизменяемого дерева, которое строится снизу вверх, это лучший выбор:
sealed interface Node permits Num, Add {}
record Num(int value) implements Node {}
record Add(Node left, Node right) implements Node {}Для узла, который перевязывают на месте, запись не подходит. Связный список с изменяемым next, дерево с обратной ссылкой на родителя, балансировка: всё это требует присваивания уже созданным полям, а компоненты записи финальны. Ссылку на родителя вдобавок невозможно заполнить в конструкторе без цикла в зависимостях. Здесь пишется обычный класс, и это нормальный ответ, а не поражение.
Где спотыкаются
Считают запись глубоко неизменяемой. Она запрещает менять ссылку, но список внутри меняется снаружи свободно, поэтому изменяемую коллекцию копируют в компактном конструкторе и наружу отдают неизменяемую.
Кладут в компонент массив. Сгенерированный equals сравнивает массивы по ссылке, и hashCode считает по ней же, так что две одинаковые по содержимому записи оказываются разными.
Забывают про рекурсию сгенерированных методов. На глубоком дереве toString и equals уходят в переполнение стека, а на структуре со ссылками назад зацикливаются, поэтому такие методы приходится писать руками.