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