Search code examples
javaserializationlambdajava-8java-runtime-compiler

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


SerializedLambda's signature is as follows:

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

Now, I can see that the capturing class is one with 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

But I didn't get what implClass is for.

Get the name of the class containing the implementation method.

Isn't the capturing class the one with the implementation method? I tried to serialize lambda functions to see what values that these fields contain but they had the same class. What is the difference between implClass and capturingClass?


Solution

  • With the current compilers, lambda expressions are always compiled to synthetic methods in the same class, holding the body of the lambda expression. For those lambda expressions, the capturing class is always identical to the “implClass”.

    But for a method reference that needs no helper method, the “implClass” will be the declaration class of the target method whereas the capturing class is the class containing the method reference.

    E.g., the following example

    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());
        }
    }
    

    prints with HotSpot/OpenJDK/javac

    cap: SerializedLambdaExample
    target: java/lang/Integer toHexString
    

    But note that the exact form may be implementation-dependent. For method references, certain constructs, e.g. involving varargs or intersection types, may get compiled using a helper method similar to lambda expressions. In theory, a lambda expression containing a trivial method invocation could get compiled like a method reference or the body could get placed into a different class, e.g. when the class file gets too large, but this does not happen in practice with the current compilers.

    Also, the example is not guaranteed to work in all runtimes. It’s only for demonstration purposes.