<div dir="ltr">There is a parallel thread going on which touches on similar things - there people are wanting "record component references." <br><br>I adapted the code from <a href="https://github.com/rilindbicaj/fluentmapper/blob/main/fluentmapper-provider/src/main/java/dev/bici/fluentmapper/provider/expression/parser/ExpressionParser.java">https://github.com/rilindbicaj/fluentmapper/blob/main/fluentmapper-provider/src/main/java/dev/bici/fluentmapper/provider/expression/parser/ExpressionParser.java</a> to use the new classfile api + work for a quick demo of the approach.<br><br>Basically, you can get at the code in the body of a lambda via some serialization hackery. The degree to which this is actually supported is to me unknown.<br><br><font face="monospace">import java.io.IOException;<br>import java.io.Serializable;<br>import java.io.UncheckedIOException;<br>import java.lang.classfile.ClassFile;<br>import java.lang.classfile.ClassModel;<br>import java.lang.classfile.instruction.FieldInstruction;<br>import java.lang.invoke.SerializedLambda;<br>import java.lang.reflect.Field;<br>import java.util.ArrayList;<br>import java.util.List;<br><br>public final class FieldReference {<br> private final Class<?> root;<br> private final List<Field> fields;<br><br> private FieldReference(Class<?> root, List<Field> fields) {<br> this.root = root;<br> this.fields = fields;<br> }<br><br><br> public Class<?> root() {<br> return root;<br> }<br><br> public List<Field> fields() {<br> return fields;<br> }<br><br> private static SerializedLambda toSerializedLambda(Expression<?, ?> expression) {<br> try {<br> var writeReplaceMethod = expression.getClass().getDeclaredMethod("writeReplace");<br> writeReplaceMethod.setAccessible(true);<br><br> return (SerializedLambda) writeReplaceMethod.invoke(expression);<br> } catch (ReflectiveOperationException e) {<br> throw new IllegalArgumentException("Could not extract SerializedLambda from the provided expression;", e);<br> }<br> }<br><br> public static <T> FieldReference of(Class<T> klass, Expression<T, ?> expression) {<br> // TODO: This work can likely be cached.<br> var serializedLambda = toSerializedLambda(expression);<br> var containingClass = serializedLambda.getImplClass();<br> var lambdaSignature = serializedLambda.getImplMethodName();<br><br> // TODO: Unsure if this is the right class loader to use<br> var classLoader = klass.getClassLoader();<br><br> byte[] classBytes;<br> try (var classResource = classLoader.getResourceAsStream(<br> containingClass.replace('.', '/') + ".class"<br> )) {<br> if (classResource == null) {<br> throw new IllegalArgumentException("Could not find class-file resource in class loader");<br> }<br><br> classBytes = classResource.readAllBytes();<br> } catch (IOException e) {<br> throw new UncheckedIOException(e);<br> }<br><br> ClassModel classModel = ClassFile.of().parse(classBytes);<br><br> var methodModel = classModel.methods().stream()<br> .filter(method -> lambdaSignature.equals(method.methodName().toString()))<br> .findFirst()<br> .orElseThrow(() -> new IllegalArgumentException("Could not find the implementing method for the expression lambda"));<br> var code = methodModel.code().orElseThrow();<br><br> // TODO: This could use a lot of prechecks<br> var fields = new ArrayList<Field>();<br><br> Class<?> lastType = klass;<br> for (var element : code.elementList()) {<br> if (element instanceof FieldInstruction fieldInstruction) {<br> try {<br> fields.add(<br> lastType.getDeclaredField(fieldInstruction.field().name().toString())<br> );<br><br><br> // TODO: This is almost certainly not sound<br> if (fieldInstruction.typeSymbol().packageName().isEmpty()) {<br> lastType = classLoader.loadClass(<br> fieldInstruction.typeSymbol().displayName()<br> );<br> }<br> else {<br> lastType = classLoader.loadClass(<br> fieldInstruction.typeSymbol().packageName() + "."<br> + fieldInstruction.typeSymbol().displayName()<br> );<br> }<br><br> } catch (NoSuchFieldException e) {<br> throw new IllegalArgumentException("Inaccessable field", e);<br> } catch (ClassNotFoundException e) {<br> throw new RuntimeException(e);<br> }<br> }<br> }<br><br><br> return new FieldReference(klass, List.copyOf(fields));<br> }<br><br><br> @FunctionalInterface<br> public interface Expression<A, B> extends Serializable {<br> B get(A a);<br> }<br><br><br> @Override<br> public String toString() {<br> return "FieldReference[" +<br> "root=" + root.getName() +<br> ", fields=" + fields.stream().map(Field::getName).toList() +<br> ']';<br> }<br>}<br></font><br>And you can use all of that with a lambda that only accesses fields to emulate "field references." <br><br><font face="monospace">class A {<br> String aa;<br> B b;<br>}<br><br>class B {<br> C c;<br>}<br><br> class C {<br> String v;<br> D d;<br>}<br><br> class D {<br> E e;<br>}<br><br> class E {<br> F f;<br>}<br><br> class F {<br> G g;<br>}<br><br> class G {<br> String v;<br>}<br><br>public class Test {<br> public static void main(String[] args) {<br> var refA = FieldReference.of(A.class, a -> a.aa);<br> IO.println(refA);<br> var refB = FieldReference.of(A.class, a -> a.b.c.d);<br> IO.println(refB);<br> }<br>}<br></font><br><font face="monospace">FieldReference[root=A, fields=[aa]]<br>FieldReference[root=A, fields=[b, c, d]]</font><div><font face="monospace"><br></font></div><div><font face="arial, sans-serif">So that is one approach to look into. </font></div></div><div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Dec 1, 2025, 8:38 PM Bruno Eberhard <<a href="mailto:bruno.eberhard@pop.ch" target="_blank">bruno.eberhard@pop.ch</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Am 01.12.2025 um 22:00 schrieb Ethan McCue:<br>
> On that note, i'd say you have somewhat of a bigger problem w.r.t. enums<br>
> [..]<br>
> As with all things in jdk.unsupported, I can't help but wonder if there <br>
> are any VM level invariants you run afoul of by making extra enum <br>
> instances. <br>
<br>
I don't know about the VM internals. I did never run into problems with <br>
these extra enums.<br>
<br>
> My first stab at #2 is to add something like this record [..]<br>
Thank you for your input. I have to try this.<br>
<br>
> I think it's also worth asking - do you have any usage statistics on <br>
> your library? (Maven central used to offer download stats, etc.) If you <br>
> are going to be broken anyways it is probably useful to know the blast <br>
> radius.<br>
<br>
Very good point. As far as I know minimal-j is only used by me for a <br>
smaller and one bigger App. In the bigger App ( An ERP called <br>
<a href="http://lisheane.ch" rel="noreferrer noreferrer" target="_blank">lisheane.ch</a> , currently only available in german ) I invested quite some <br>
time. I would have to rewrite parts of it.<br>
<br>
So the world will not be standing still if minimal-j doesn't work <br>
anymore or has to be changed.<br>
<br>
I would still like the idea of field references ( Person::name , <br>
Person::address::city ) in the java language as equivalent for the $ <br>
constant. It is really nice to use. But I don't have the knowledge how <br>
to add it to the language or how to propose the feature.<br>
</blockquote></div></div></div>