An experiment: retrieve record component from accessor literal via code reflection

Tagir Valeev amaembo at gmail.com
Tue Dec 2 09:58:24 UTC 2025


Hello!

A recent discussion in amber-dev [1] lead me to the following example how
people may use Code Reflection.
The problem:
Retrieve a RecordComponent object from the corresponding record accessor
method reference. Expect the following API:

private record MyRecord(int comp, double comp2) {}
...
RecordComponent component = ReflectUtil.component(MyRecord::comp);

It looks like it's possible to solve this with Code Reflection. My solution
is the following:

package org.example;

import jdk.incubator.code.Quoted;
import jdk.incubator.code.Reflect;
import jdk.incubator.code.dialect.core.CoreOp;
import jdk.incubator.code.dialect.java.JavaOp;
import jdk.incubator.code.dialect.java.MethodRef;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;

public final class ReflectUtil {
    @Reflect
    @FunctionalInterface
    public interface RecordAccessor<T extends Record> {
        /**
         * @param r record type
         * @return result of the record component
         * @param <R> an unused generic parameter to avoid accidental use
with lambda
         *           (lambdas cannot bind to generic SAM).
         *           Only method references are supported.
         */
        <R> Object get(T r);
    }

    /**
     * @param accessor a method reference bound to a record accessor
     * @return record component that corresponds to a given accessor
     * @param <T> record type
     * @throws IllegalArgumentException if the supplied argument is not a
record accessor method reference
     * @throws RuntimeException if the record accessor cannot be resolved
(e.g., due to insufficient access rights)
     */
    public static <T extends Record> RecordComponent
component(RecordAccessor<T> accessor) {
        Quoted quoted = CoreOp.QuotedOp.ofQuotable(accessor)
                .orElseThrow(() -> new IllegalArgumentException("Accessor
must be quotable"));
        if (!(quoted.op() instanceof JavaOp.LambdaOp lambda)) {
            throw new IllegalArgumentException("Expected method reference");
        }
        JavaOp.InvokeOp invokeOp = lambda.methodReference()
                .orElseThrow(() -> new IllegalArgumentException("Expected
method reference"));
        if (invokeOp.invokeKind() != JavaOp.InvokeOp.InvokeKind.INSTANCE) {
            throw new IllegalArgumentException("Method reference bound to
record accessor is expected");
        }
        MethodRef descriptor = invokeOp.invokeDescriptor();
        Method method;
        try {
            method =
descriptor.resolveToMethod(MethodHandles.privateLookupIn(accessor.getClass(),
MethodHandles.lookup()));
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException("Cannot resolve descriptor
"+descriptor, e);
        }
        Class<?> recordClass = method.getDeclaringClass();
        if (!recordClass.isRecord()) {
            throw new IllegalArgumentException("Not a record accessor
method");
        }
        return Arrays.stream(recordClass.getRecordComponents()).filter(rc
-> rc.getAccessor().equals(method))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Not a
record accessor: "+method));
    }
}

I'm not completely sure if privateLookupIn is the right thing to do here.
Otherwise, it's quite a pleasant experience: the code is simple and
self-explanatory. LambdaOp::methodReference was very helpful.

With best regards,
Tagir Valeev


[1] https://mail.openjdk.org/pipermail/amber-dev/2025-November/009472.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/babylon-dev/attachments/20251202/4472bae5/attachment-0001.htm>


More information about the babylon-dev mailing list