Other |
---|
tbd_majorUnresolved |
Duplicate :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
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.
|