RFR: 8374654: Inconsistent handling of lambda deserialization for Object method references on interfaces

Liam Miller-Cushon cushon at openjdk.org
Thu Jan 22 11:18:25 UTC 2026


On Wed, 7 Jan 2026 18:43:54 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:

>> See [JDK-8374654](https://bugs.openjdk.org/browse/JDK-8374654). This fixes an inconsistency between generated Lambda `$deserialization$` methods and the runtime behaviour of `SerializedLambda` instances, for Object method references on interfaces. At runtime the methods are resolved to declared methods, so Object method references on interfaces will always have the `getImplMethodKind`, `getImplClass`, and `getImplMethodSignature` of the method declared in `Object`, not of an override in the interface.
>
> (I am sorry for a longish post, but I can't write this short.)
> 
> There's a lot of history in this, and even though I tried to page some of it back, I may still be missing some pieces.
> 
> In short, as far as I know, there are quite a few cases where (de)serialization of method references misbehaves. And changes around this area tend to cause regressions, often regressions that are found quite late.
> 
> In this specific case, I think the simple example that could be used as a motivation for this PR is (based on your code and code from PR #28943):
> 
> $ cat Main.java; jdk-25/bin/java Main.java
> /**
>  * @test
>  * @compile Main.java
>  * @run main Main
>  */
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.ObjectInputStream;
> import java.io.ObjectOutputStream;
> import java.io.Serializable;
> 
> public class Main {
>     public static void main(String... args) throws Exception {
>         F<I2, Integer> r2 = (F<I2, Integer>) I2::hashCode;
>         ((F<I2, Integer>) serialDeserial(r2)).apply(new I2() {});
>     }
> 
>     @SuppressWarnings("unchecked")
>     static <T> T serialDeserial(T object) throws Exception {
>         ByteArrayOutputStream baos = new ByteArrayOutputStream();
>         ObjectOutputStream oos = new ObjectOutputStream(baos);
>         oos.writeObject(object);
>         oos.close();
>         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
>         ObjectInputStream ois = new ObjectInputStream(bais);
>         T result = (T) ois.readObject();
>         ois.close();
>         return result;
>     }
> 
>     interface I1 extends Serializable {}
> 
>     interface I2 extends I1 {
>         @Override
>         public int hashCode();
>     }
> 
>     interface F<T, R> extends Serializable {
>         R apply(T t);
>     }
>     
> } Exception in thread "main" java.io.InvalidObjectException: ReflectiveOperationException during deserialization
>         at java.base/java.lang.invoke.SerializedLambda.readResolve(SerializedLambda.java:269)
>         at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
>         at java.base/java.lang.reflect.Method.invoke(Method.java:565)
>         at java.base/java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1066)
>         at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2142)
>         at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1620)
>         at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.ja...

@lahodaj @dansmithcode any more thoughts on this?

I have applied these changes to Google's JDK build and haven't seen any regressions. The only affected test I found was explicitly testing for JDK-8208752. I recognize this is a subtle area and there's a risk of regressions, but I think that this change together with #28943 might be an improvement over the status quo.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/29075#issuecomment-3783850602


More information about the compiler-dev mailing list