【问题标题】:Change field in Record更改记录中的字段
【发布时间】:2023-01-27 02:58:36
【问题描述】:

我研究反射并尝试更改 Record 中字段的值。

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();
        }
    }
}

如果我将 Record 转换为 Class 一切正常,但是使用 Record 我会得到异常

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(用于练习)并使用数据库中的数据创建记录。但是记录将有指向其他记录和记录列表的链接,然后我将不得不把记录创建彻底翻过来。这更难,我希望避免它。

标签: java reflection record


【解决方案1】:

一般来说,评论者说这“无法完成”或“不可能”并没有错......除非你愿意稍微改变 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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多