【问题标题】:Java 8 lambdas that access instance fields and methods can't be deserialized无法反序列化访问实例字段和方法的 Java 8 lambda
【发布时间】:2014-07-13 06:50:04
【问题描述】:

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

以下代码按原样运行良好,但如果我取消注释第二个 runnable 初始化,直接使用“this”,它无法反序列化对象(in.readObject() 引发异常)。

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)

这是预期的行为吗?

【问题讨论】:

  • 旁注:不需要额外的接口,直接写:Runnable runnable = (Runnable & Serializable) () -> self.say();
  • 我知道。我只是想澄清一下这个东西是可序列化的,而不是另一个“我如何序列化 lambdas”的问题。不够清楚,似乎:)
  • 除了最明显的以外,我什么都试过了。问题发生在 Eclipse 中(其中对 java 8 的支持仍处于测试阶段),但不在 javac 中。因此,一个 JDT 错误。
  • 为我工作(在 Eclipse 中)。您确定您运行的是最新版本吗?过去有 lambda 序列化的 bug,我自己向 Eclipse 团队提交了一些,但很快就修复了。

标签: java serialization lambda deserialization java-8


【解决方案1】:

我尝试了所有方法,但最明显的除外。

问题发生在 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

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

【讨论】:

  • 这是我自己删除问题的情况吗?
  • 你应该离开它,以防有人遇到同样的问题。不过,您应该接受自己的答案。
  • 在谈论软件中的错误时,您应该始终添加准确的版本信息。使用Kepler SR2 + Eclipse JDT/Java 8 support 1.0.0.v20140317-1956,您的示例可以正常工作。
  • @Holger 是的!观察得好。我已经编辑了答案以添加信息。谢谢
  • 我使用的是 Eclipse SDK 版本:Luna SR2 (4.4.2) Build id: M20141210-0900,这个问题已经修复了。
【解决方案2】:

这……有点奇怪。

这就是the documentation 所说的关于序列化 lambda 的内容:

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

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

当进一步探索这条路径时,我们看到TestClass 需要可序列化,这似乎是因为它实现了Serializable。此外,它将使用默认的 lambda 序列化(这通常不是一个好主意),并且它有一个 String 和一个 SerializableRunnable 作为参数,它们都是 Serializable

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

【讨论】:

  • 我不明白你所说的使用“默认 lambda 序列化”“通常不是一个好主意”的意思。对于如何序列化 lambda,没有选择。
猜你喜欢
  • 2019-07-06
  • 1970-01-01
  • 1970-01-01
  • 2020-11-26
  • 2019-09-22
  • 2016-04-11
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多