An experiment: retrieve record component from accessor literal via code reflection
David Alayachew
davidalayachew at gmail.com
Tue Dec 2 16:28:52 UTC 2025
Thanks for putting this together Tagir. It's exciting to see what babylon
will be able to provide us if stuff like this is fairly easy to do.
On Tue, Dec 2, 2025, 4:59 AM Tagir Valeev <amaembo at gmail.com> wrote:
> 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/f3187bf6/attachment.htm>
More information about the babylon-dev
mailing list