Значение "поверхностно неизменяемый" в документации Record в Java 14

Я читаю документацию Records и не понимаю термин "поверхностно неизменяемый". Что мы подразумеваем под " поверхностно неизменным"? А если он неизменен, зачем нам конструктор копирования? Почему два "Hello Worlds!"?

Для всех классов записей должен выполняться следующий инвариант: если компоненты записи R - c1, c2, ... cn, то если экземпляр записи копируется следующим образом:

 R copy = new R(r.c1(), r.c2(), ..., r.cn());  // copy constructor ?

тогда должно быть так, что r.equals(copy).

2 ответа

Решение

Неглубоко неизменяемые означает, что если у класса есть поля, эти поля рассматриваются как final. Однако их поля (т.е. поля полей) не обязательно должны бытьfinal.

Вам не нужно реализовывать конструктор, он уже реализован для вас таким образом. Но если вы решите реализовать его самостоятельно, например, для проверки аргумента, этот инвариант должен сохраниться.

Если вы рассматриваете класс как составную часть или иерархию других классов и примитивов (целые числа, массивы и т. Д.), Неглубокая неизменяемость относится к неизменяемости (постоянству) только первого уровня.

Это контрастирует с термином "глубокая неизменность", который относится к неизменности всей иерархии. Большинство ощутимых преимуществ, которые вы слышите о неизменяемости, таких как неявная потокобезопасность, применимы только к чему-то глубоко неизменяемому.

Рассмотрим этот класс

class Foo {
    private final MutableBar bar;

    //ctor, getter
}

Этот класс поверхностно неизменяем. Его нельзя изменить напрямую, но можно изменить косвенно, например

foo.getBar().setSomeProperty(5);

так что это не является глубоко неизменным.

Еще один пример неглубокой неизменяемости с использованием только примитивов

class Foo {
    private final int[] ints;

    Foo(int[] ints) {
        this.ints = ints;
    }
}

Это можно видоизменить так

int[] ints = {1};
Foo foo = new Foo(ints);
ints[0] = 2;

Для небольшой иерархии иногда просто сделать неглубоко неизменяемый класс глубоко неизменяемым. Обычно это включает в себя защитные копии или переключение изменяемых классов на неизменяемые варианты.

class Foo {
    private final int[] ints; 

    Foo(int[] ints) {
        // copy to protect against the kind of mutation shown above
        this.ints = Arrays.copyOf(ints, ints.length);
    }

    // if you must have a getter for an array, make sure not to return the array itself, 
    // otherwise the caller can change it.
    // for performance reasons, consider an immutable List instead - no copy required
    int[] getInts() {
        return Arrays.copyOf(ints, ints.length);
    }
}
Другие вопросы по тегам