Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialization and deserialization of lambda

Tags:

java

java-8

This code below throws

Exception in thread "main" java.lang.ClassCastException: test.Subclass2 cannot be cast to test.Subclass1
at test.LambdaTest.main(LambdaTest.java:17)

public class LambdaTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ToLongFunction<B> fn1 = serde((ToLongFunction<B> & Serializable) B::value);
        ToLongFunction<C> fn2 = serde((ToLongFunction<C> & Serializable) C::value);
        fn1.applyAsLong(new B());
        fn2.applyAsLong(new C()); // Line 17 -- exception here!
    }

    private static <T extends Serializable> T serde(T t) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        new ObjectOutputStream(bos).writeObject(t);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos
                .toByteArray()));
        return (T) ois.readObject();
    }
}

class A {
    public long value() {
        return 0;
    }
}

class B extends A { }

class C extends A { }

The reason seems to be that after serialization and deserialization, both fn1 and fn2 end up as the same class. Is this a JDK/compiler bug or am I missing something about serialization and deserialization of lambdas?

like image 219
Can Gencer Avatar asked Feb 17 '18 11:02

Can Gencer


People also ask

Can lambda be serialized?

Serialization is a process for writing the state of an object into a byte stream so that we can transfer it over the network. We can serialize a lambda expression if its target type and its captured arguments have serialized.

How can lambda expression be serialized?

You can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner classes, the serialization of lambda expressions is strongly discouraged.

What is serialization and deserialization explain with example?

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object. The byte stream created is platform independent.

What is serialization and deserialization of data?

Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization.


1 Answers

Have a look at this Open JDK issue raised back in 2016:

Deserialization of lambda causes ClassCastException

It quite precisely matches your scenario:

  • Two (distinct) classes, B and C, both of which extend the same base class, A, which has a method, String f().
  • Create a Supplier reference to method f() for an object of type B; call this bf [new B()::f].
  • Create a Supplier reference to method f() for an object of type C; cal this cf [new C()::f].
  • Serialize cf (ObjectOutputStream#writeObject)
  • When the serialized cf is deserialized (ObjectInputStream#readObject), a ClassCastException is thrown saying that class C cannot be cast to class B

There's an interesting discussion on the issue, but the very last comment by Dan Smith seems to nail it:

Important observation for this particular test case: the "qualifying type" (i.e., the class named by the bytecode) of a method reference should be the same as the qualifying type of an invocation: the type of the receiver. javac is wrong to be using the type of the declaring class. See JDK-8059632.

Fix that bug, and I think the issue with different captured types goes away.

like image 149
Marko Topolnik Avatar answered Sep 19 '22 15:09

Marko Topolnik