if a lambda is a field and captures `this`, and it's deserialized into a new class loader, it throws a ClassCastException. i came across this bug independently while writing a test, but then found an old openjdk bug with a similar issue and tweaked the test case (source below). my version differs only in 1. moved the lambda to a field 2. reference `this` in it 3. uses readAllBytes instead of the internal method 4. formatting
running with java 11 or 12 (or java 8 using a substitute for readAllBytes) results in: this: test.SerializedLambdaTest$MyCode@8efb846 deSerializedThis: test.SerializedLambdaTest$MyCode@2b71fc7e runnable: test.SerializedLambdaTest$MyCode$$Lambda$1/0x0000000801188440@37bba400 Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.lang.invoke.SerializedLambda to field test.SerializedLambdaTest$MyCode.runnable2 of type test.SerializedLambdaTest$SerializableRunnable in instance of test.SerializedLambdaTest$MyCode at java.base/java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2190) at java.base/java.io.ObjectStreamClass$FieldReflector.checkObjectFieldValueTypes(ObjectStreamClass.java:2153) at java.base/java.io.ObjectStreamClass.checkObjFieldValueTypes(ObjectStreamClass.java:1407) at java.base/java.io.ObjectInputStream.defaultCheckFieldValues(ObjectInputStream.java:2371) at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2278) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594) at java.base/java.io.ObjectInputStream.readArray(ObjectInputStream.java:1993) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1588) at java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355) at java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2249) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430) at test.SerializedLambdaTest$MyCode.deserialize(SerializedLambdaTest.java:35) at test.SerializedLambdaTest$MyCode.run(SerializedLambdaTest.java:51) at test.SerializedLambdaTest.main(SerializedLambdaTest.java:66) https://bugs.openjdk.java.net/browse/JDK-8008770 package test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; // from: https://bugs.openjdk.java.net/browse/JDK-8008770 public class SerializedLambdaTest { public interface SerializableRunnable extends Runnable,Serializable { } public static class MyCode implements SerializableRunnable { SerializableRunnable runnable2 = () -> { System.out.println("HELLO"+this.getClass()); }; private byte[] serialize(Object o) { ByteArrayOutputStream baos; try ( ObjectOutputStream oos = new ObjectOutputStream(baos = new ByteArrayOutputStream())) { oos.writeObject(o); } catch (IOException e) { throw new RuntimeException(e); } return baos.toByteArray(); } private <T> T deserialize(byte[] bytes) { try ( ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { return (T) ois.readObject(); } catch (IOException|ClassNotFoundException e) { throw new RuntimeException(e); } } @Override public void run() { System.out.println(" this: "+this); SerializableRunnable deSerializedThis = deserialize(serialize(this)); System.out.println(" deSerializedThis: " +deSerializedThis); SerializableRunnable runnable = runnable2; System.out.println(" runnable: "+runnable); SerializableRunnable deSerializedRunnable = deserialize(serialize(runnable)); System.out.println("deSerializedRunnable: " +deSerializedRunnable); } } public static void main(String[] args) throws Exception { ClassLoader myCl = new MyClassLoader( SerializedLambdaTest.class.getClassLoader() ); Class<?> myCodeClass = Class.forName( SerializedLambdaTest.class.getName()+"$MyCode", true, myCl ); Runnable myCode = (Runnable) myCodeClass.newInstance(); myCode.run(); } static class MyClassLoader extends ClassLoader { MyClassLoader(ClassLoader parent) { super(parent); } @Override protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException { if (name.startsWith("test.")) synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c==null) c = findClass(name); if (resolve) resolveClass(c); return c; } else return super.loadClass(name,resolve); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String path = name.replace('.','/').concat(".class"); try ( InputStream is = getResourceAsStream(path)) { if (is!=null) { byte[] bytes = is.readAllBytes(); return defineClass(name,bytes,0,bytes.length); } else throw new ClassNotFoundException(name); } catch (IOException e) { throw new ClassNotFoundException(name,e); } } } }