A minor irritation when using JNA structures is that they are mutable (and I mean totally mutable) since by default all fields must be public and cannot be final (though see below). This means that if we would like to expose a JNA structure as a DTO (data transfer object) the client can basically monkey with the data.
Consider this trivial example:
class PhysicalDevice {
private VkPhysicalDeviceLimits limits;
public VkPhysicalDeviceLimits limits() {
if(limits == null) {
limits = ... // Instantiate from API using JNA
}
return limits; // <-- Anyone can fiddle with this fully mutable data!
}
}
Here the VkPhysicalDeviceLimits is a JNA structure retrieved from the native library (thread synchronization omitted for clarity). The accessor is lazy but the same issue applies whether it's lazy, created as a member explicitly, injected by a framework, or whatever.
If the structure consisted of just a handful of fields then we would obviously create a wrapper that properly encapsulated the underlying JNA data performing any defensive copying required. Unfortunately this structure has over a hundred fields, all of which have to be public and mutable - writing a wrapper by hand would be tedious and error prone.
Of course we could simply retrieve an instance of the structure on each invocation of the limits() accessor but in general we want to avoid unnecessary calls to the native layer and ideally the data should be cached locally.
Couple of other notes:
The structure is code-generated from the native C header file but could be re-generated if changes were needed.
The data does not change over the lifetime of the application, i.e. it's basically static data.
There are several other (equally large) structures in this API.
So how to encapsulate this data?
I can't see any way of 'cloning' a JNA structure (for example) using a copy constructor or other means? We could do a field-by-field copy using reflection but that feels dirty?
Can the fields be made final? This is mentioned as 'risky' in some of the JNA documentation but I'm unsure why.
Any suggestions?