这是一个最小的工作示例(需要 Java 22 或更高版本):
(仅使用 libc
free
来阐述行为)
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public class Main {
public static final Linker nativeLinker = Linker.nativeLinker();
public static final SymbolLookup stdlibLookup = nativeLinker.defaultLookup();
public static final SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
private static final FunctionDescriptor DESCRIPTOR$free = FunctionDescriptor.ofVoid(ValueLayout.ADDRESS);
private static final MethodHandle HANDLE$free =
loaderLookup.find("free")
.or(() -> stdlibLookup.find("free"))
.map(symbolSegment -> nativeLinker.downcallHandle(symbolSegment, DESCRIPTOR$free))
.orElseThrow(() -> new RuntimeException("libc function free not found but y?"));
public static void free(MemorySegment address) {
try {
// I want to allow user code to use Java null and MemorySegment.NULL (C NULL) interchangeably
HANDLE$free.invokeExact(address != null ? address : MemorySegment.NULL);
} catch (Throwable throwable) {
throwable.printStackTrace(System.err);
}
}
public static void main(String[] args) {
free(null); // free(MemorySegment.NULL) will produce same result
}
}
运行程序会出现异常:
java.lang.invoke.WrongMethodTypeException: handle's method type (MemorySegment)void but found (Object)void
at java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:521)
at java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:530)
at Main.free(Main.java:19)
at Main.main(Main.java:26)
Java 抱怨用于调用
invokeExact
的参数是 Object
,而不是 MemorySegment
。然而,表达
address != null ? address : MemorySegment.NULL
当然应该有类型
MemorySegment
。如果我们对代码做一点小小的修改:
public static void free(MemorySegment address) {
try {
MemorySegment temp = address != null ? address : MemorySegment.NULL;
HANDLE$free.invokeExact(temp);
} catch (Throwable throwable) {
throwable.printStackTrace(System.err);
}
}
它工作正常。
还有
HANDLE$free.invoke(address != null ? address : MemorySegment.NULL)
有效HANDLE$free.invokeExact((MemorySegment)(address != null ? address : MemorySegment.NULL))
有效,但是HANDLE$free.invokeExact((address != null ? address : MemorySegment.NULL))
不起作用这是某种故意设计、实现限制还是 JVM 错误?
请注意,由于
invokeExact
是签名多态的,因此编译器的工作是根据参数表达式确定您尝试调用的方法的方法类型。如果编译器发现不匹配的方法类型,则表达式在运行时计算为什么类型并不重要。
确实,条件表达式(三元运算符表达式)的类型是
Object
。
来自 JLS 15.25.3,
如果引用条件表达式出现在赋值上下文或调用上下文中,则它是聚合表达式(第 5.2 节。第 5.3 节)。否则,它是一个独立的表达式。
...
多引用条件表达式的类型与其目标类型相同。
如果
HANDLE$free.invokeExact(address != null ? address : MemorySegment.NULL);
条件运算符的目标类型是
Object
,因为 invokeExact
被声明为采用 Object...
。
如果您在将表达式传递给
MemorySegment
之前将其分配给 invokeExact
类型的变量,则参数表达式实际上就是 MemorySegment
类型的名称表达式。