繁体   English   中英

JNA 结构可以支持不变性吗?

[英]Can a JNA structure support immutability?

使用 JNA 结构时的一个小麻烦是它们是可变的(我的意思是完全可变的),因为默认情况下所有字段都必须是公共的并且不能是final的(尽管见下文)。 这意味着如果我们想将 JNA 结构公开为 DTO(数据传输对象),客户端基本上可以处理数据。

考虑这个简单的例子:

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!
    }
}

这里的VkPhysicalDeviceLimits是从本地库中检索到的 JNA 结构(为清楚起见,省略了线程同步)。 访问器是惰性的,但同样的问题适用于它是否是惰性的、显式创建为成员、由框架注入或其他。

如果该结构仅包含几个字段,那么我们显然会创建一个包装器,该包装器可以正确封装底层 JNA 数据,以执行所需的任何防御性复制。 不幸的是,这个结构有一百多个字段,所有这些字段都必须是公开的和可变的——手工编写包装器会很乏味并且容易出错。

当然,我们可以在每次调用limits()访问器时简单地检索结构的实例,但通常我们希望避免对本机层的不必要调用,理想情况下数据应该在本地缓存。

其他几点注意事项:

  • 该结构是从本机 C header 文件代码生成的,但如果需要更改,可以重新生成。

  • 数据在应用程序的生命周期内不会改变,即它基本上是 static 数据。

  • 这个 API 中还有其他几个(同样大的)结构。

那么如何封装这些数据呢?

我看不到使用复制构造函数或其他方式“克隆”JNA 结构(例如)的任何方式? 我们可以使用反射进行逐个字段的复制,但这感觉很脏?

字段可以final吗? 这在一些 JNA 文档中被称为“有风险”,但我不确定为什么。

有什么建议么?

根据 JNA API ,除了 JNA 的read()方法从本机 memory 填充该结构外, final修饰符将使字段只读。

结构字段可能还具有以下修饰符:

final JNA 将通过read()覆盖该字段,否则该字段不可从 Java 修改。 使用此选项时要小心,因为编译器通常会假定对字段的所有访问(对于给定的 Structure 实例)具有相同的值。 此修饰符在 J2ME 上无效。

注意字段是否可以更改值; 如果底层 memory 发生变化并重新读取,则可以。 理论上,用户可以通过以下方式绕过只读性质:

  • 调用结构的getPointer()
  • 在适当的偏移量处直接写入本机 memory
  • 调用结构的read()方法。

这是不可变的吗? 可能不会,但这与反射访问可以绕过final修饰符的现有非模块化限制没有什么不同。 它至少应该防止“意外”修改值,我认为这是不变性的意图。

一种开销很小的可能想法是将Structure子类化为类似于ImmutableStructure的东西,您可以在其中覆盖read() 最初它允许将值读取到final字段中,但是您可以在将 boolean 返回给用户之前更改它,从而将read()变为无操作。 这仍然允许反射访问(如任何 Java 类),但增加了防止无意修改的进一步步骤。

如果您不想 go 这条路线并想要一个真正的防御性副本,则您不得不放弃结构 API 和 go 的便利性,该结构直接从数组/字节缓冲区的本机方法返回并处理. 您可以通过计算字段顺序、获取字段的偏移量(甚至将它们缓存在 map 中供以后使用)以及使用只读 getter 直接读取您关心的偏移量处的字节值来执行类似于Structure class 的操作。

通过反射的逐字段复制(或从本机读取)发生在结构的指针构造函数的底层,因此创建防御性副本可以像读取结构大小的字节数组一样简单,创建一个新的Memory object 并写入将这些字节传递给它,并将指向该Memory的指针传递给新副本的构造函数以通过super(p)使用。 可能有更有效的方法来做到这一点。

您也可以考虑简单地序列化 object 并将其反序列化为新的 object。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM