There is a way to do this, but it isn't pretty. Use annotation processing.

@GeneratedRecordComponentInterface
record User(String firstName, String lastName, ComplexObject
complexColumn) implements UserColumns {}

// annotation processor generates:
public interface UserColumns {
  public static final RecordComponentWrapper<String>
FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class,
"firstName");
  public static final RecordComponentWrapper<String>
LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class,
"lastName");
  public static final RecordComponentWrapper<ComplexObject>
COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class,
"complexColumn");
}

where `RecordComponentWrapper` is some suitable type-safe wrapper for
record components.

Although there is a string for each record component, it is in
generated code, thus won't get out of sync.

(This is the annotation processing equivalent to how Joda-Beans
meta-properties have worked for many years)
Stephen

On Sun, 30 Nov 2025 at 20:39, David Alayachew <[email protected]> wrote:
>
> Thanks everyone for the context. The answers I am getting are very helpful. I 
> do see a comment about XY problem, so here is the full context of my problem.
>
> Unlike normal classes, record are transparent data carriers. Which make them 
> useful for simple DTO's and serialization. Specifically, I wanted to create a 
> De/serialization format for a CSV file.
>
> Let's say I have record User(String firstName, String lastName, ComplexObject 
> complexColumn) {}, and each RecordComponent corrsponds to a column of my CSV, 
> while each instance of User corresponds to a single row. Let's also assume 
> that firstName and lastName are computationally cheap to deserialize, but 
> complexColumn is expensive (complex but necessary regex).
>
> Well, I want to create a CSV De/Serialization tool that allows me to not only 
> deserialize the object in full, but also deserialize only the specific 
> component I want. In this case, if all I care about is firstName and 
> lastName, then why should I pay the price of also deserializing 
> complexColumn, which I won't use at all?
>
> Hence why I requested these various things in the thread -- such as the 
> ability to reference RecordComponents, or why I wanted to use a method 
> reference to a records accessors. I wanted some way to get compile time type 
> safety about the column I am requesting. If I change my User data type such 
> that firstName now says first instead, I want all callers of my tool to get 
> compile time errors. But I don't see how to do it.
>
> I really do think that, if records claim to be transparent carriers of data, 
> then being able to isolate and point to a single RecordComponent in a type 
> safe way that will fail at compile time if I am out of sorts sounds like a 
> reasonable thing to want from a record.
>
>
> On Sun, Nov 30, 2025 at 3:04 PM Kirill Semyonkin <[email protected]> wrote:
>>
>> Hello David,
>>
>> I've seen some people (ab)use Serializable and SerializedLambda to get names 
>> of the class and the method, with numerous examples like 
>> https://stackoverflow.com/a/53397378/16208077. From there you can traverse 
>> usual reflection with those strings to get the exact RecordComponent or some 
>> other reflection object. But those strings come from users doing method 
>> references, so you will get the behavior that you wanted, at least to some 
>> extent. Although it slightly feels like an XY problem.
>>
>> - Kirill Semyonkin
>>
>> вс, 30 нояб. 2025 г. в 22:57, Attila Kelemen <[email protected]>:
>>>
>>> I guess, if your record type is fixed, then you can have a dummy instance 
>>> of that record with all fields being different (hopefully, no 3 boolean 
>>> components :)), and then you can do the same hack as with the interface + 
>>> proxy combo.
>>>
>>> Attila Kelemen <[email protected]> ezt írta (időpont: 2025. nov. 
>>> 30., V, 20:51):
>>>>
>>>> I think that is impossible. What I did when I needed something similar is 
>>>> that I used an interface instead of a record and built utilities around 
>>>> it, because in that case you can imitate what you want.
>>>>
>>>> That is, consider that you have `interface User { String firstName(); 
>>>> String lastName() }`. In this case, you could have methods taking a 
>>>> `Function<User, T>` and then you can create an instance of `User` via 
>>>> `Proxy.newProxyInstance` in which you record which method was called, and 
>>>> pass the proxy instance to the `Function`. Assuming that someone passed 
>>>> `User::firstName` for the `Function`, you can detect which method was 
>>>> passed. The drawbacks are obvious, but it is better than passing strings 
>>>> in my opinion.
>>>>
>>>> David Alayachew <[email protected]> ezt írta (időpont: 2025. nov. 
>>>> 30., V, 20:41):
>>>>>
>>>>> Thanks for the response Attila.
>>>>>
>>>>> Let me try and loosen the constraints then -- is there any way for me to 
>>>>> get a RecordComponent corresponding to firstName without needing to do 
>>>>> String comparison?
>>>>>
>>>>> At the end of the day, that's all that I really need.
>>>>>
>>>>> As is now, the only way I can see to get a RecordComponent is to drill 
>>>>> down from j.l.Class --> j.l.r.RecordComponent, then do String comparison 
>>>>> against a provided String to get the record component that I want. That 
>>>>> is unsafe and stringly typed, so, very much undesirable. After all, if I 
>>>>> change my record to say first instead of firstName, I want a compiler 
>>>>> error. But doing it that way, I won't -- I'll get a runtime error.
>>>>>
>>>>> So that's what I really want -- a type-safe way to isolate a record 
>>>>> component from a record, without forcing my users to have to provide a 
>>>>> String corresponding to the record component name.
>>>>>
>>>>> On Sun, Nov 30, 2025 at 2:16 PM Attila Kelemen 
>>>>> <[email protected]> wrote:
>>>>>>
>>>>>> I'm pretty sure there is no such thing, because that essentially implies 
>>>>>> the existence of some kind of method literal (well, it would not be 
>>>>>> strictly necessary, but the JLS would feel strange without it), and 
>>>>>> there is no such thing (though it would be awesome, if there was).
>>>>>>
>>>>>> Also, note that if this was a thing, then your case is just a very 
>>>>>> special case, and you would want more (I would for sure). That is, in 
>>>>>> that case, I would also want type safety. So, something like 
>>>>>> MethodReference<(MyRecord) -> String> (which of course would require 
>>>>>> function types in Java).
>>>>>>
>>>>>> When I needed this, luckily I could restrain my need to interface 
>>>>>> methods (as opposed to your record getters) where I could create a 
>>>>>> `Proxy` and see which method gets called (nasty hack, has its downsides, 
>>>>>> but felt like the safest to me).
>>>>>>
>>>>>> Attila
>>>>>>
>>>>>> David Alayachew <[email protected]> ezt írta (időpont: 2025. nov. 
>>>>>> 29., Szo, 20:50):
>>>>>>>
>>>>>>> And by all means, add more parameters to foo if you want. For example, 
>>>>>>> if a User.class helps, please do so!
>>>>>>>
>>>>>>> I just don't want to do anything like this.
>>>>>>>
>>>>>>> foo("firstName");
>>>>>>>
>>>>>>> That's stringly typed, and undesirable for my use case.

Reply via email to