[英]Serialization and deserialization of lambda
This code below throws 以下代码抛出
Exception in thread "main" java.lang.ClassCastException: test.Subclass2 cannot be cast to test.Subclass1
at test.LambdaTest.main(LambdaTest.java:17)
public class LambdaTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ToLongFunction<B> fn1 = serde((ToLongFunction<B> & Serializable) B::value);
ToLongFunction<C> fn2 = serde((ToLongFunction<C> & Serializable) C::value);
fn1.applyAsLong(new B());
fn2.applyAsLong(new C()); // Line 17 -- exception here!
}
private static <T extends Serializable> T serde(T t) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(t);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos
.toByteArray()));
return (T) ois.readObject();
}
}
class A {
public long value() {
return 0;
}
}
class B extends A { }
class C extends A { }
The reason seems to be that after serialization and deserialization, both fn1 and fn2 end up as the same class. 原因似乎是在序列化和反序列化之后,fn1和fn2都以同一个类结束。 Is this a JDK/compiler bug or am I missing something about serialization and deserialization of lambdas?
这是一个JDK /编译器错误还是我错过了关于lambdas的序列化和反序列化的一些内容?
Have a look at this Open JDK issue raised back in 2016: 看看2016年提出的Open JDK问题:
Deserialization of lambda causes ClassCastException lambda的反序列化导致ClassCastException
It quite precisely matches your scenario: 它非常精确地匹配您的场景:
- Two (distinct) classes,
B
andC
, both of which extend the same base class,A
, which has a method,String f()
.两个(不同的)类,
B
和C
,它们都扩展了相同的基类A
,它有一个方法String f()
。- Create a
Supplier
reference to methodf()
for an object of typeB
;为类型
B
的对象创建方法f()
的Supplier
引用; call thisbf
[new B()::f
].叫这个
bf
[new B()::f
]。- Create a
Supplier
reference to methodf()
for an object of typeC
;为类型为
C
的对象创建方法f()
的Supplier
引用; cal thiscf
[new C()::f
].cal这个
cf
[new C()::f
]。- Serialize
cf
(ObjectOutputStream#writeObject
)序列化
cf
(ObjectOutputStream#writeObject
)- When the serialized
cf
is deserialized (ObjectInputStream#readObject
), aClassCastException
is thrown saying that classC
cannot be cast to classB
当反序列化序列化
cf
(ObjectInputStream#readObject
)时,ClassCastException
,说C
类不能转换为B
类
There's an interesting discussion on the issue, but the very last comment by Dan Smith seems to nail it: 关于这个问题有一个有趣的讨论,但丹史密斯最后的评论似乎指出:
Important observation for this particular test case: the "qualifying type" (ie, the class named by the bytecode) of a method reference should be the same as the qualifying type of an invocation: the type of the receiver.
对此特定测试用例的重要观察:方法引用的“限定类型”(即字节码命名的类)应与调用的限定类型相同:接收器的类型。 javac is wrong to be using the type of the declaring class.
javac使用声明类的类型是错误的。 See JDK-8059632 .
见JDK-8059632 。
Fix that bug, and I think the issue with different captured types goes away.
修复该错误,我认为不同捕获类型的问题消失了。
I don't know how to explain that but the behavior is particular because executing the main code with only one invocation of serde()
works for both cases. 我不知道如何解释,但行为是特别的,因为执行主代码只有一个
serde()
调用适用于这两种情况。
That is this : 就是这样:
ToLongFunction<B> fn1 = serde((ToLongFunction<B> & Serializable) A::value);
fn1.applyAsLong(new B());
or this : 或这个 :
ToLongFunction<C> fn2 = serde((ToLongFunction<C> & Serializable) A::value);
fn2.applyAsLong(new C());
While as the two invocations are performed in the same program, I notice an expected thing : the value of the parameter ( T t
) is not the same for the two cases. 虽然两个调用是在同一个程序中执行的,但我注意到了一个预期的事情:参数(
T t
)的值对于这两种情况并不相同。
But I notice an unexpected thing : at the second invocation of serde()
the object returned by ois.readObject()
is the same reference as which one returned by the first ois.readObject()
invocation. 但我注意到一个意想不到的事情:在第二次调用
serde()
返回的对象ois.readObject()
是作为由第一返回的一个相同的附图ois.readObject()
调用。
As if the first one was cached and reused for the second invocation. 好像第一个被缓存并重新用于第二次调用。
It seems a bug. 这似乎是一个错误。
If you want to make it work, then remove the default implementation of value()
from BaseClass
interface and override it in the implementing classes like this: 如果你想让它工作,那么从
BaseClass
接口中删除value()
的默认实现,并在实现类中覆盖它,如下所示:
interface BaseClass {
long value();
}
public class LambdaTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ToLongFunction<Subclass1> fn1 = serDe((Serializable & ToLongFunction<Subclass1>) Subclass1::value);
fn1.applyAsLong(new Subclass1());
ToLongFunction<Subclass2> fn2 = serDe((Serializable & ToLongFunction<Subclass2>) Subclass2::value);
fn2.applyAsLong(new Subclass2());
}
private static <T extends Serializable> void printType(T t) {
System.out.println(t.getClass().getSimpleName());
}
private static <T extends Serializable> T serDe(T t) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(t);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
T readT = (T) ois.readObject();
ois.close();
bos.close();
return readT;
}
}
class Subclass1 implements BaseClass {
@Override
public long value() {
return 0;
}
}
class Subclass2 implements BaseClass {
@Override
public long value() {
return 1;
}
}
I hope it helps. 我希望它有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.