访问实例字段和方法的Java 8 lambda无法反序列化
在我看来,这是编译器或JVM中的一个错误,但也许有人有更好的解释。
下面的代码运行正常,但如果我取消注释第二个runnable
初始化,它直接使用’this’,它不能反序列化对象( in.readObject()
抛出exception)。
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)
这是预期的行为吗?
我尝试了一切,但最明显。
问题发生在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
也许它已经在更新的版本中得到纠正。
那是……相当奇怪。
这就是文档中关于序列化lambdas的内容:
如果lambda表达式的目标类型及其捕获的参数是可序列化的,则可以序列化它。 但是,与内部类一样,强烈建议不要对lambda表达式进行序列化。
我并不完全熟悉捕获的参数,但我假设它指的是lambda捕获的所有元素,这意味着它引用了this
,因此它是一个捕获的元素。
当进一步探索该路径时,我们发现TestClass
需要是可序列化的,它似乎是在实现Serializable
。 此外,它将使用默认的lambda序列化(这不是一个好主意),它有一个String
和一个SerializableRunnable
作为参数,它们都是Serializable
。
所以在我看来你已经遇到了JVM中的一个错误,它可能是由于目标等于捕获的参数( this
)。