ClassCastException
von Java8 geworfen wird eine Lambda auf Deserialisieren wenn folgende Bedingungen erfüllt sind:Java8 Lambda Deserialisierung - Classcast
- Geordnete Klasse eine Methode hat, deren Ausnutzung automatisch verwendet wird, eine
Serializable
Lambda - Es zu erstellen sind mehrere untergeordnete Klassen, die es erweitern, und es gibt mehrere Verwendungen der oben genannten Methode als eine Methodenreferenz, aber mit verschiedenen untergeordneten Klassen
- Nachdem Methodenreferenz verbraucht wird, wird es serialisiert und deserialisiert
- Alle Methodenreferenzen werden innerhalb der gleichen Erfassungsklasse verwendet
Getestet auf Oracle Java-Compiler und Laufzeitversionen 1.8.0_91. finden Sie Testcode auf, wie zu reproduzieren:
import java.io.*;
/**
* @author Max Myslyvtsev
* @since 7/6/16
*/
public class LambdaSerializationTest implements Serializable {
static abstract class AbstractConverter implements Serializable {
String convert(String input) {
return doConvert(input);
}
abstract String doConvert(String input);
}
static class ConverterA extends AbstractConverter {
@Override
String doConvert(String input) {
return input + "_A";
}
}
static class ConverterB extends AbstractConverter {
@Override
String doConvert(String input) {
return input + "_B";
}
}
static class ConverterC extends AbstractConverter {
@Override
String doConvert(String input) {
return input + "_C";
}
}
interface MyFunction<T, R> extends Serializable {
R call(T var);
}
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.version"));
ConverterA converterA = new ConverterA();
ConverterB converterB = new ConverterB();
ConverterC converterC = new ConverterC();
giveFunction(converterA::convert);
giveFunction(converterB::convert);
giveFunction(converterC::convert);
}
private static void giveFunction(MyFunction<String, String> f) {
f = serializeDeserialize(f);
System.out.println(f.call("test"));
}
private static <T> T serializeDeserialize(T object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
@SuppressWarnings("unchecked")
T result = (T) ois.readObject();
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Es gibt folgende Ausgabe:
1.8.0_91
test_A
Exception in thread "main" java.lang.RuntimeException: java.io.IOException: unexpected exception type
at LambdaSerializationTest.serializeDeserialize(LambdaSerializationTest.java:68)
at LambdaSerializationTest.giveFunction(LambdaSerializationTest.java:52)
at LambdaSerializationTest.main(LambdaSerializationTest.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.io.IOException: unexpected exception type
at java.io.ObjectStreamClass.throwMiscException(ObjectStreamClass.java:1582)
at java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1154)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1817)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at LambdaSerializationTest.serializeDeserialize(LambdaSerializationTest.java:65)
... 7 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.lang.invoke.SerializedLambda.readResolve(SerializedLambda.java:230)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1148)
... 11 more
Caused by: java.lang.ClassCastException: LambdaSerializationTest$ConverterB cannot be cast to LambdaSerializationTest$ConverterA
at LambdaSerializationTest.$deserializeLambda$(LambdaSerializationTest.java:7)
... 21 more
diese $deserializeLambda$
Methode mit CFR folgenden Code Nach decompiling enthüllt wird:
private static /* synthetic */ Object $deserializeLambda$(SerializedLambda lambda) {
switch (lambda.getImplMethodName()) {
case "convert": {
if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("LambdaSerializationTest$MyFunction") && lambda.getFunctionalInterfaceMethodName().equals("call") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("LambdaSerializationTest$AbstractConverter") && lambda.getImplMethodSignature().equals("(Ljava/lang/String;)Ljava/lang/String;")) {
return (MyFunction<String, String>)LambdaMetafactory.altMetafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, convert(java.lang.String), (Ljava/lang/String;)Ljava/lang/String;)((ConverterA)((ConverterA)lambda.getCapturedArg(0)));
}
if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("LambdaSerializationTest$MyFunction") && lambda.getFunctionalInterfaceMethodName().equals("call") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("LambdaSerializationTest$AbstractConverter") && lambda.getImplMethodSignature().equals("(Ljava/lang/String;)Ljava/lang/String;")) {
return (MyFunction<String, String>)LambdaMetafactory.altMetafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, convert(java.lang.String), (Ljava/lang/String;)Ljava/lang/String;)((ConverterB)((ConverterB)lambda.getCapturedArg(0)));
}
if (lambda.getImplMethodKind() != 5 || !lambda.getFunctionalInterfaceClass().equals("LambdaSerializationTest$MyFunction") || !lambda.getFunctionalInterfaceMethodName().equals("call") || !lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") || !lambda.getImplClass().equals("LambdaSerializationTest$AbstractConverter") || !lambda.getImplMethodSignature().equals("(Ljava/lang/String;)Ljava/lang/String;")) break;
return (MyFunction<String, String>)LambdaMetafactory.altMetafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, convert(java.lang.String), (Ljava/lang/String;)Ljava/lang/String;)((ConverterC)((ConverterC)lambda.getCapturedArg(0)));
}
}
throw new IllegalArgumentException("Invalid lambda deserialization");
}
es also Es erscheint, dass das tatsächliche erfasste Argument nicht verwendet wird, um zu bestimmen, welches exakte Lambda deserialisiert werden muss. Alle 3 lambdas erfüllen den 1. if
Zustand und ConverterA
werden angenommen.
Beim Debuggen wir können das lambda.getCapturedArg(0)
einer korrekten Art im laufenden Betrieb beobachten ist (ConverterB
wenn Ausnahme ausgelöst wird) und lohnt sich auch, dass gegossene Feststellung nicht, da Verfahren benötigt wird, aufgerufen werden soll, die in Basis AbstractConverter
Klasse.
Ist es erwartetes Verhalten? Wenn ja, was ist eine empfohlene Problemumgehung?
Auf meinem PC funktioniert es wie ein Zauber mit Ausgang: 1,8. 0_60 test_A test_B test_C –
Könnte es plattformspezifisch sein? Ich benutze Mac OS. –
Ich benutze auch Mac :) –