JDK-8174865 : Different method references deserialize to the same class
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u101,9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2017-02-13
  • Updated: 2022-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_majorUnresolved
Related Reports
Relates :  
Relates :  
Relates :  
Description
The SerializedLambda class doesn't encode the extra interfaces and method descriptors provided to 'altMetafactory'. As a result, method references that refer to the same method but with different target types can end up being deserialized to the same class, leaving one of the deserialized objects with either too many or too few interfaces/bridges.

--------

Test:

import java.util.Arrays;
import java.io.*;

public class LambdaSerialization {

    interface I {}

    public static void main(String... args) throws Exception {
        Runnable r1 = (Runnable & Serializable) System::gc;
        Runnable r2 = (Runnable & Serializable & I) System::gc;
        test(r1);
        System.out.println();
        test(r2);
    }
    
    public static void test(Object o1) throws Exception {
        System.out.printf("Starting object %s implements %s%n", o1, Arrays.asList(o1.getClass().getInterfaces()));
        try (ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
                ObjectOutputStream out = new ObjectOutputStream(outBytes)) {
            out.writeObject(o1);
            out.flush();
            try (ByteArrayInputStream inBytes = new ByteArrayInputStream(outBytes.toByteArray());
                    ObjectInputStream in = new ObjectInputStream(inBytes)) {
                Object o2 = in.readObject();
                System.out.printf("Deserialized object %s implements %s%n", o2, Arrays.asList(o2.getClass().getInterfaces()));
            }
        }
    }

}

This program prints:

Starting object LambdaSerialization$$Lambda$1/989110044@404b9385 implements [interface java.lang.Runnable, interface java.io.Serializable]
Deserialized object LambdaSerialization$$Lambda$4/284720968@b4c966a implements [interface java.lang.Runnable, interface java.io.Serializable]

Starting object LambdaSerialization$$Lambda$2/1096979270@2f4d3709 implements [interface java.lang.Runnable, interface LambdaSerialization$I, interface java.io.Serializable]
Deserialized object LambdaSerialization$$Lambda$4/284720968@b4c966a implements [interface java.lang.Runnable, interface java.io.Serializable]

--------

The API deficiency is reported in JDK-8174864. Possible ways forward:
- Wait for JDK-8174864 to be addressed, then adapt $deserializeLambda$ code-gen logic
- Detect when a serialized form clash will occur (different LambdaMetafactory parameters, same SerializedLambda), and generate a lambda bridge for the second occurrence
- *Always* generate a lambda bridge when, say, FLAG_BRIDGES or FLAG_MARKERS would be needed.