2016-07-06 471 views
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类。

预期行为?如果是,建议的解决方法是什么?

+0

在我的PC它就像与输出的魅力:1.8。 0_60 test_A test_B test_C –

+0

它可能是平台特定的吗?我正在使用Mac OS。 –

+0

我也在使用Mac :) –

回答

4

甲骨文已经确认这是一个错误,分配如下错误ID:JDK-8161257

现在是官方追踪可见:JDK-8161257