Добавить окончательный модификатор во время выполнения в Java

Это может показаться немного странным, но можно ли добавить модификатор final во время выполнения?

У меня есть переменная, которая помечена как public static int/short. В какой-то момент я хочу предотвратить изменение его значения, и я хочу сохранить его доступность как стандартное статическое значение (ClassName.field).

public class Main {

    private static int a = 0;

    public static void addFinalMod(Field f) {

        Field modifiersField = null;
        try {
            modifiersField = Field.class.getDeclaredField("modifiers");
        }
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        modifiersField.setAccessible(true);
        try {
            modifiersField.setInt(f, f.getModifiers() & Modifier.FINAL);
        }
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        System.out.println(a);
        try {
            Field f = Main.class.getDeclaredField("a");
            addFinalMod(f);
        }
        catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        a = 10; //I was expecting error/exception here
        System.out.println(a);
    }

Вывод:

0
10

Ответы

Ответ 1

В какой-то момент я хочу предотвратить изменение значения

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

final - это ключевое слово/функция языка для написания исходного кода и предотвращения повторного назначения переменной в исходном коде. Во время выполнения ничего (почти) ничего не делает.

Ответ 2

Используйте неизменяемый объект:

public class Thing {
    private int value;
    public Thing(int value) { this.value = value; }
    public int getValue() { return this.value; }
}

Затем просто замените его, когда он нуждается в модификации:

Thing x = Thing(1);
// do some stuff
x = Thing(2);
// more stuff

Если вы думаете: "Но тогда они могут изменить значение, заменив объект!" Да, конечно. Единственный способ предотвратить это - заблокировать некоторое глобальное состояние (что на самом деле невозможно, если оно не является константой), но вы не используете глобальное состояние, верно? Глобальное состояние - это Bad Thing TM.

Глобальное состояние имеет очень простую проблему: он делает ваш код гораздо менее многоразовым и более подвержен ошибкой. Ошибка, подверженная ошибкам, особенно актуальна, если вы изменяете это глобальное состояние во время выполнения. "Ой, дерьмо, все происходит в неправильном порядке". Вы не представляете, насколько это легко. Попытка отслеживать порядок событий - причина, по которой mutlithreading настолько сложна. Что касается возможности повторного использования, это означает, что у вас не может быть двух независимых экземпляров вещи, что, в свою очередь, подразумевает, что у вас не может быть никаких двух экземпляров всего, что от этого зависит (по крайней мере, когда вам оказывается, вам нужно эта вещь в конце концов).

Даже если это не глобально, тогда это какое-то странное состояние, когда каждый, кто должен изменить ваш код (включая себя), должен знать и думать о том, как они вносят изменения. Похоже, что это будет делать изменения проще или сложнее? (Подсказка: последняя.) Не делайте этого самому себе или своим коллегам. Сделайте это проще. Сделайте урок из Zen of Python: "Простой лучше, чем сложный".

Итак, нижняя строка: если вам нужен неизменный объект, используйте неизменяемый объект. Если вам нужен неизменный глобальный элемент, который не является константой, выясните, как лучше организовать код.