#java #arity #java-record #java-16
#java #arity #java-запись #java-16
Вопрос:
Пробуем некоторый код с record
компонентами и записываем. Я использовал компоненты переменной редкости и был поражен ошибкой времени компиляции из-за пользовательского конструктора.
public record Break<R extends Record>(R record, String... notifications) {
public Break(R record, String... notifications) {
System.out.println("record: " record " and notifications: " Arrays.toString(notifications));
this.record = record;
this.notifications = notifications;
}
// compile error: non canonical record constructor must delegate to another costructor
public Break(R record) {
System.out.println("record: " record);
this.record = record;
}
public Break() {
this(null); // this works
// actually intelliJ suggests it uses the constructor that is not compiling
}
public static void main(String[] args) {
new Break<>(new Break<>());
}
}
Что мне интересно понять, так это то, как выводится аналогичный конструктор при вызове через другой пользовательский конструктор без каких-либо компонентов, предоставленных для инициализации.
Комментарии:
1. Я понимаю, что неканонические конструкторы НЕ обязаны делегировать только каноническому конструктору. Java все еще может разрешать цепочку и обнаруживать циклы. Итак, предполагая, что вы
Break(R record)
делегированы каноническому конструктору, вызовBreak()
все равно приведет к успешной инициализации всех компонентов.
Ответ №1:
Это не имеет ничего общего с переменной арностью. Все цепочки конструкторов записей должны быть «снизу» в каноническом конструкторе (который может быть указан с использованием компактной формы), независимо от того, является ли переменная арностью или нет. Это проявляется как требование, чтобы каждый неканонический конструктор должен делегировать другому конструктору; в конечном итоге это означает, что цепочки находятся внизу в каноническом.
Вы можете исправить свой плохой конструктор с помощью:
public Break(R record) {
this(record);
}
Но вам даже не нужно объявлять этот конструктор all, поскольку канонический конструктор уже может обрабатывать вызовы формы new Break(r)
.
Комментарии:
1. Иногда я хотел бы иметь частные конструкторы, которые обходят канонический конструктор. Причины включают в себя отказ от создания защитной копии массива, если я знаю, что переданный массив не будет изменен в будущем. Может быть, путем явного вызова
super()
, чтобы показать, что это не случайность? Текущий обходной путь использует ThreadLocals для установки флага, но обычно это не стоит таких усилий.2. @JohannesKuhn Я думаю, вы обнаружите, что такие игры в основном просто перемещают проблему. Ваши средства доступа выполняют защитные копии? Вы обновляете
Object::equals
, чтобы отразить это? Обратите внимание, что существует уточненный контракт,Record::equals
который, вероятно, подрывается подобными трюками.3. Я всегда спрашиваю себя: что, если
java.lang.String
бы это была запись? Имеет смысл из нескольких POV. Чего я стараюсь избегать, так это двойного копирования массива, поэтому я хочу реализовать такой конструктор. Глядя на эволюцию String, становится ясно, что также должна быть история о том, как преобразовать запись в обычный класс. Проблема в отражающей поддержке —Class.getRecordComponents
больше не работает.4. @JohannesKuhn Может быть, способ сделать это — написать класс? Помните, что записи не являются синтаксической функцией (хотя их синтаксис хорош), они являются семантической функцией — номинальными кортежами. Все (почти), что могут делать записи, могут делать классы (кроме реализации
java.lang.Record
.)5. @JohannesKuhn Я согласен, что текущее определение может исключать некоторые (согласованные: редкие) случаи, которые все равно будут семантически кандидатами. И в еще более редких случаях разница в производительности может иметь значение (но я подозреваю, что это очень редко.) Но одного этого все еще недостаточно, чтобы сделать вывод, что мы выбрали неправильный набор правил; выбор правил требует балансировки множества различных соображений, и это только одно из них. Дизайн предполагает компромиссы; здесь мы исключаем некоторые возможные хорошие случаи, чтобы исключить больше плохих случаев. Спросите меня через 5 лет, правильно ли мы это поняли…