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