6
ClassCastException
由Java8在反序列化时满足以下条件的λ抛出:Java8 LAMBDA反序列化ClassCastException异常
- 父类有一个方法,参考其用于自动地创建
Serializable
拉姆达 - 有是扩展它的几个子类,并且上述方法有几种用法作为方法参考,但是具有不同的子类
- 在使用方法引用之后,它将被序列化并且被反序列化了
- 所有方法参考均在同一捕获类中使用
在Oracle Java编译器和运行时版本1.8.0_91上进行测试。 请有关如何重现找到测试代码:
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);
}
}
}
它提供了以下的输出:
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
在反编译这个$deserializeLambda$
法CFR以下代码揭晓:
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");
}
所以似乎并没有使用实际捕获的参数来确定哪个精确的lambda必须被反序列化。所有3个lambda将满足1st if
条件,并且将假定ConverterA
。
调试时我们可以观察到,在运行时lambda.getCapturedArg(0)
是一个正确的类型(ConverterB
时抛出异常)的并且还值得注意的是不需要的该铸造方法以来被调用存在于基AbstractConverter
类。
预期行为?如果是,建议的解决方法是什么?
在我的PC它就像与输出的魅力:1.8。 0_60 test_A test_B test_C –
它可能是平台特定的吗?我正在使用Mac OS。 –
我也在使用Mac :) –