Java SerializedLambda 的 capturingClass 和 implClass 之间有什么区别?

what are the differences between capturingClass and implClass for Java SerializedLambda?

SerializedLambda的签名如下:

SerializedLambda(Class capturingClass, String functionalInterfaceClass, String functionalInterfaceMethodName, String functionalInterfaceMethodSignature, int implMethodKind, String implClass, String implMethodName, String implMethodSignature, String instantiatedMethodType, Object[] capturedArgs)

现在,我可以看到捕获 class 是一个 deserializeLambda:

SerializedLambda has a readResolve method that looks for a (possibly private) static method called $deserializeLambda$(SerializedLambda) in the capturing class, invokes that with itself as the first argument

但我不明白 implClass 是干什么用的。

Get the name of the class containing the implementation method.

捕获class不就是有实现方法的吗?我尝试序列化 lambda 函数以查看这些字段包含哪些值,但它们具有相同的 class。 implClasscapturingClass 有什么区别?

对于当前的编译器,lambda 表达式总是在相同的 class 中编译为合成方法,包含 lambda 表达式的主体。对于那些 lambda 表达式,捕获 class 始终与“implClass”相同。

但对于不需要辅助方法的方法引用,“implClass”将是目标方法的声明 class 而捕获 class 是包含方法参考。

例如下面的例子

public class SerializedLambdaExample {
    public static void main(String[] args) throws ReflectiveOperationException {
        var hex = (IntFunction<String>&Serializable)Integer::toHexString;
        Method m = hex.getClass().getDeclaredMethod("writeReplace");
        m.setAccessible(true);
        SerializedLambda sl = (SerializedLambda)m.invoke(hex);
        System.out.println("cap: "+sl.getCapturingClass());
        System.out.println("target: "+sl.getImplClass()+" "+sl.getImplMethodName());
    }
}

打印 HotSpot/OpenJDK/javac

cap: SerializedLambdaExample
target: java/lang/Integer toHexString

但请注意,确切的形式可能取决于实现。对于方法引用,某些构造,例如涉及可变参数或交集类型,可能会使用类似于 lambda 表达式的辅助方法进行编译。理论上,包含简单方法调用的 lambda 表达式可以像方法引用一样进行编译,或者正文可以放入不同的 class,例如当 class 文件变得太大时,但这在当前编译器的实践中不会发生。

此外,不保证该示例在所有运行时都有效。仅供演示之用。