FULL PRODUCT VERSION :
java version "1.8.0_73"
Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]
A DESCRIPTION OF THE PROBLEM :
- 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
The problem seems to be with the generate byte code (using javap to decompile):
- javac generates a call to invokevirtual that references the shared based class (invokevirtual SerializationTest$A.f:()Ljava/lang/String;)
- the Eclipse compiler generates a call invokevirtual that references the actual class the lambda was created from
-- javac generated --
0: #109 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#110 ()Ljava/lang/Object;
#111 invokevirtual SerializationTest$A.f:()Ljava/lang/String;
#112 ()Ljava/lang/String;
#113 5
#114 0
1: #109 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#110 ()Ljava/lang/Object;
#111 invokevirtual SerializationTest$A.f:()Ljava/lang/String;
#112 ()Ljava/lang/String;
#113 5
#114 0
-- Eclipse compiler generated --
BootstrapMethods:
0: #172 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#173 ()Ljava/lang/Object;
#176 invokestatic SerializationTest.lambda$0:(LSerializationTest$B;)Ljava/lang/String;
#177 ()Ljava/lang/String;
#178 1
1: #172 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#179 ()Ljava/lang/Object;
#182 invokestatic SerializationTest.lambda$1:(LSerializationTest$C;)Ljava/lang/String;
#183 ()Ljava/lang/String;
#178 1
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the program below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No ClassCastException when the lambda is deserialized.
ACTUAL -
See exception below.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.io.IOException: unexpected exception type
at java.io.ObjectStreamClass.throwMiscException(Unknown Source)
at java.io.ObjectStreamClass.invokeReadResolve(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at scratch.SerializationTest.main(SerializationTest.java:31)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.lang.invoke.SerializedLambda.readResolve(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
... 5 more
Caused by: java.lang.ClassCastException: SerializationTest$C cannot be cast to SerializationTest$B
at SerializationTest.$deserializeLambda$(SerializationTest.java:10)
... 14 more
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;
public class SerializationTest {
static class A implements Serializable {
public String f() { return toString(); }
}
static class B extends A { }
static class C extends A { }
public static void main(String[] args) throws Exception {
Supplier<String> bs = (Supplier<String> & Serializable) new B()::f;
Supplier<String> cs = (Supplier<String> & Serializable) new C()::f;
ByteArrayOutputStream caos = new ByteArrayOutputStream();
try (ObjectOutputStream coos = new ObjectOutputStream(caos);) {
coos.writeObject(cs);
}
try (ObjectInputStream cis = new ObjectInputStream(new ByteArrayInputStream(caos.toByteArray()));) {
Supplier<String> ccs = (Supplier<String>) cis.readObject();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't use multiple lambdas that reference the same method in a base class and that get serialized and deserialized in the same class.
java version "1.8.0_73"
Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]
A DESCRIPTION OF THE PROBLEM :
- 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
The problem seems to be with the generate byte code (using javap to decompile):
- javac generates a call to invokevirtual that references the shared based class (invokevirtual SerializationTest$A.f:()Ljava/lang/String;)
- the Eclipse compiler generates a call invokevirtual that references the actual class the lambda was created from
-- javac generated --
0: #109 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#110 ()Ljava/lang/Object;
#111 invokevirtual SerializationTest$A.f:()Ljava/lang/String;
#112 ()Ljava/lang/String;
#113 5
#114 0
1: #109 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#110 ()Ljava/lang/Object;
#111 invokevirtual SerializationTest$A.f:()Ljava/lang/String;
#112 ()Ljava/lang/String;
#113 5
#114 0
-- Eclipse compiler generated --
BootstrapMethods:
0: #172 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#173 ()Ljava/lang/Object;
#176 invokestatic SerializationTest.lambda$0:(LSerializationTest$B;)Ljava/lang/String;
#177 ()Ljava/lang/String;
#178 1
1: #172 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#179 ()Ljava/lang/Object;
#182 invokestatic SerializationTest.lambda$1:(LSerializationTest$C;)Ljava/lang/String;
#183 ()Ljava/lang/String;
#178 1
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the program below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No ClassCastException when the lambda is deserialized.
ACTUAL -
See exception below.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.io.IOException: unexpected exception type
at java.io.ObjectStreamClass.throwMiscException(Unknown Source)
at java.io.ObjectStreamClass.invokeReadResolve(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at scratch.SerializationTest.main(SerializationTest.java:31)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at java.lang.invoke.SerializedLambda.readResolve(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
... 5 more
Caused by: java.lang.ClassCastException: SerializationTest$C cannot be cast to SerializationTest$B
at SerializationTest.$deserializeLambda$(SerializationTest.java:10)
... 14 more
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;
public class SerializationTest {
static class A implements Serializable {
public String f() { return toString(); }
}
static class B extends A { }
static class C extends A { }
public static void main(String[] args) throws Exception {
Supplier<String> bs = (Supplier<String> & Serializable) new B()::f;
Supplier<String> cs = (Supplier<String> & Serializable) new C()::f;
ByteArrayOutputStream caos = new ByteArrayOutputStream();
try (ObjectOutputStream coos = new ObjectOutputStream(caos);) {
coos.writeObject(cs);
}
try (ObjectInputStream cis = new ObjectInputStream(new ByteArrayInputStream(caos.toByteArray()));) {
Supplier<String> ccs = (Supplier<String>) cis.readObject();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't use multiple lambdas that reference the same method in a base class and that get serialized and deserialized in the same class.
- duplicates
-
JDK-8161257 Java8 Lambda Deserialization - ClassCastException
- Closed
- relates to
-
JDK-8174865 Different method references deserialize to the same class
- Open
-
JDK-8059632 Method reference compilation uses incorrect qualifying type
- Resolved
-
JDK-8282080 Lambda deserialization fails for Object method references on interfaces
- Closed
-
JDK-8008413 Reduce size of $deserializeLambda$ method
- Open
-
JDK-8174864 SerializedLambda does not preserve markers & bridges
- In Progress
(1 relates to)