访问实例字段和方法的Java 8 lambda无法反序列化

时间:2014-05-23 19:38:19

标签: java serialization lambda deserialization java-8

在我看来,这是编译器或JVM中的一个错误,但也许有人有更好的解释。

以下代码可以正常运行,但是如果我取消注释第二个runnable初始化,它直接使用'this',它就不能反序列化对象(in.readObject()抛出异常)。< / p>

public class TestClass implements Serializable {
    String               msg = "HEY!";
    SerializableRunnable runnable;
    public TestClass() {
        TestClass self = this;
        runnable = () -> self.say();  // uses a local copy of 'this'
       // runnable = () -> this.say(); // uses 'this' directly
    }
    public void say() {
        System.out.println(msg);
    }
    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (ObjectOutputStream out = new ObjectOutputStream(buffer)) {
            out.writeObject(new TestClass());
        }
        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray()))) {
            TestClass s = (TestClass) in.readObject();
            s.say();
        }
    }
}
interface SerializableRunnable extends Runnable, Serializable {
}

这是根本原因的堆栈跟踪:

java.lang.IllegalArgumentException: Invalid lambda deserialization
    at j8test.TestClass.$deserializeLambda$(TestClass.java:1)
    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:483)
    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:483)
    at java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1104)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1810)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1993)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1918)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at j8test.TestClass.main(TestClass.java:30)

这是预期的行为吗?

2 个答案:

答案 0 :(得分:6)

我尝试了一切,但最明显。

问题发生在Eclipse(其中java 8支持仍处于测试阶段),但不是在javac中。因此,一个JDT错误。

<强> [编辑]

我正在跑步:

Eclipse IDE for Java and Report Developers
Version: Luna RC1 Release (4.4.0RC1)
Build id: 20140522-1310

Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

OS X 10.9.3

也许它已经在更新的版本中得到纠正。

答案 1 :(得分:3)

那是......相当奇怪。

这是the documentation关于序列化lambdas的内容:

  

如果lambda表达式的目标类型及其捕获的参数是可序列化的,则可以序列化它。但是,与内部类一样,强烈建议不要对lambda表达式进行序列化。

我并不完全熟悉捕获的参数,但我假设它指的是lambda捕获的所有元素,在这种情况下它指的是this,所以它是一个然后捕获元素。

当进一步探索该路径时,我们发现TestClass需要是可序列化的,它似乎是在实现Serializable时。此外,它将使用默认的lambda序列化(这不是一个好主意),它有StringSerializableRunnable作为参数,它们都是Serializable。< / p>

所以在我看来你已经遇到了JVM中的一个错误,它可能是由于目标等于被捕获的参数(this)。