Почему на super метод records нельзя сослаться

Почему на суперметоды record нельзя сослаться? Допустим у меня есть класс:

public record Test(String str1,String str2) {
    @Override //Не выдает ошибку
    public String str1() {
        return super.str1(); //Cannot resolve method 'str1' in 'Record'
    }
}

На момент написания его конечно нет, но при этом:

  public static void main(String[] args) {
       Test test = new Test("строка1","строка2");
       String str1 = test.str1();
       String str2 = test.str2(); //Оба работают корректно
   }

test.str2(), test.str2() и @Override работают корректно то-есть находят его, но при super.str1() выдается ошибка Cannot resolve method 'str1' in 'Record'


Ответы (1 шт):

Автор решения: Nowhere Man

Почему на суперметоды record нельзя сослаться?

Коротко, "потому что их нет"(с) Майор Пейн.

Кортежи record представляют собой финальные неизменяемые классы-обёртки (data class) для хранения и передачи данных, чтобы заменить привычные POJO классы (бины).

Фактически они неявно наследуются от абстрактного класса java.lang.Record, в котором, разумеется, не определены геттеры для компонентов кортежа. В данном случае имеет место попытка вызвать геттер, отсутствующий в классе-предке, обращаясь к нему при помощи super, о чём и сообщает компилятор.

Более того, у кортежей не определены даже стандартные методы класса Object: equals, hashCode, toString -- все они являются абстрактными! Таким образом, для кортежа нельзя также написать что-то вроде super.hashCode() или super.toString().

Кроме того, такое переопределение геттера само по себе не слишком хорошо и его можно считать дурнопахнущим кодом (code smell)), так как есть риск прочитать значение, которое отличается от записанного значения, что также сломает контракт Record::equals.

@Override //Не выдает ошибку

Дело в том, что сама аннотация @Override была модифицирована для геттеров в кортежах, для чего была изменено её описание в спецификации языка Java: JLS 9.6.4.4: Annotation Interface :: @Override:

If a method declaration in class or interface Q is annotated with @Override, then one of the following three conditions must be true, or a compile-time error occurs:

  • the method overrides from Q a method declared in a supertype of Q (§8.4.8.1, §9.4.1.1)

  • the method is override-equivalent to a public method of Object (§4.3.2, §8.4.2)

  • Q is a record class (§8.10), and the method is an accessor method for a record component of Q (§8.10.3)

Также об этом упоминалось в JEP 395: Records:

  • The meaning of the @Override annotation was extended to include the case where the annotated method is an explicitly declared accessor method for a record component.

Абстрагируясь от полезности переопределения геттера для компонента кортежа, можно сказать, что такая аннотация и переопределение позволят разве что отследить изменение / удаление элемента кортежа и потребовать изменения / удаления соответствующего геттера, о чём также говорится в JLS:

The use of @Override on the accessor method int x() ensures that if the record component x is modified or removed, then the corresponding accessor method must be modified or removed too.

Разумеется, при переопределении нельзя будет использовать ключевое слово super, но можно иcпользовать ссылку на текущий компонент кортежа this.x, или даже завести локальную переменную с тем же названием:

record Roo(int x, int y) {
    @Override
    public int x() {
        return Math.abs(this.x);
    }

    @Override
    public int y() {
        int y = 5;
        return y * this.y;  // за такое могут казнить на code review
    }

}
→ Ссылка