Я изучаю отражение и пытаюсь изменить значение поля в записи.
public record Account(Integer id, String login, Boolean blocked) {}
public class Main {
public static void main(String[] args) {
Account account = new Account(null, null, null);
setFieldValue(account, "id", 1);
setFieldValue(account, "login", "admin");
setFieldValue(account, "blocked", false);
System.out.println(account);
}
public static void setFieldValue(Object instance,
String fieldName,
Object value) {
try {
Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, value);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
Если я конвертирую запись в класс, все работает, но с записью я получаю исключение
java.lang.IllegalAccessException: Can not set final java.lang.Integer field Account.id to java.lang.Integer
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79)
at java.base/java.lang.reflect.Field.set(Field.java:799)
Что мне нужно сделать, чтобы код работал с записями?
То, что вы хотите, невозможно. record
объекты неизменяемы. В лучшем случае вы можете создать новый экземпляр Account
с примененными изменениями. Если объект Account
где-то хранится, вы можете перезаписать поле (если это не final
) того, что хранит учетную запись, с этим новым экземпляром учетной записи.
Hotspot доверяет последним полям в записи, поэтому вы не можете их изменить. См. спецификацию для Field.set — «Если базовое поле является окончательным, этот объект Field имеет доступ для записи тогда и только тогда, когда выполняются следующие условия: ... класс объявления поля не является классом записи».
Как насчет... инициализации полей в конструкторе?
@ dan1st Я надеялся реализовать свой собственный ORM (для практики) и создавать записи с данными из базы данных. Но запись будет иметь ссылки на другие записи и список записей, и тогда мне придется вывернуть создание записи наизнанку. Это сложнее, я надеялся избежать этого.
См. определение записи.
Нет, для создания record
из строки базы данных вам не нужно выворачивать создание наизнанку. Вы должны перестать выворачивать его наизнанку. Широкое распространение не превращает плохую практику в хорошую.
В общем, комментаторы, говорящие, что это «не может быть сделано» или «невозможно», не ошибаются... если только вы не готовы немного изменить правила JVM:) Например, используя небезопасное отражение для изменения соответствующее значение непосредственно в ячейке памяти в записи следующим образом:
public static void setFieldValue(Object instance, String fieldName, Object value) {
try {
Field f = instance.getClass().getDeclaredField(fieldName);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
Field theInternalUnsafeField = Unsafe.class.getDeclaredField("theInternalUnsafe");
theInternalUnsafeField.setAccessible(true);
Object theInternalUnsafe = theInternalUnsafeField.get(null);
Method offset = Class.forName("jdk.internal.misc.Unsafe").getMethod("objectFieldOffset", Field.class);
unsafe.putBoolean(offset, 12, true);
unsafe.putObject(instance, (long) offset.invoke(theInternalUnsafe, f), value);
} catch (IllegalAccessException | NoSuchFieldException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
Не возвращайтесь с жалобами на сбои или «странное поведение» в вашей программе.
@Holger Я принял этот ответ, но не потому, что буду его использовать. Я не хотел удалять вопрос. Вдруг кто-то тоже будет искать эту информацию.
@ЭдуардАртюх именно поэтому важно напомнить будущим читателям, что этот код — не решение, а путь к катастрофе.
Вы создаете новую запись с нужными вам изменениями. Вы НЕ МОЖЕТЕ изменить уже существующий экземпляр.