为什么java.util.Optional不是Serializable,如何用这些字段序列化对象

Enum类是可序列化的,因此使用枚举序列化对象没有问题。 另一种情况是类有java.util.Optional类的字段。 在这种情况下,会引发以下exception:java.io.NotSerializableException:java.util.Optional

如何处理这样的类,如何序列化它们? 是否有可能发送这样的对象到远程EJB或通过RMI?

这是一个例子:

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Optional; import org.junit.Test; public class SerializationTest { static class My implements Serializable { private static final long serialVersionUID = 1L; Optional<Integer> value = Optional.empty(); public void setValue(Integer i) { this.i = Optional.of(i); } public Optional<Integer> getValue() { return value; } } //java.io.NotSerializableException is thrown @Test public void serialize() { My my = new My(); byte[] bytes = toBytes(my); } public static <T extends Serializable> byte[] toBytes(T reportInfo) { try (ByteArrayOutputStream bstream = new ByteArrayOutputStream()) { try (ObjectOutputStream ostream = new ObjectOutputStream(bstream)) { ostream.writeObject(reportInfo); } return bstream.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } } 

这个答案是对标题“不应该是可序列化的?”中的问题的回应。 简短的回答是,Java Lambda(JSR-335)专家组考虑并拒绝了它 。 这个注释,以及这个和这个表明, Optional的主要devise目标是当返回值可能不存在时被用作函数的返回值。 意图是调用者立即检查Optional并提取实际值,如果它存在。 如果该值不存在,则调用者可以替代默认值,抛出exception或应用其他策略。 这通常是通过在streampipe道(或其他方法)的末尾链接stream程方法调用来完成的,该方法返回Optional值。

它从来没有打算用Optional方式来使用,例如用于可选的方法参数或将其作为字段存储在对象中 。 而且,通过使Optional序列化可以将其永久存储或通过networking传输,这两者都鼓励使用远远超出其最初的devise目标。

通常有更好的方法来组织数据,而不是在一个字段中存储一个可Optional 。 如果getter(例如问题中的getValue方法)从字段返回实际的Optional ,它将强制每个调用者实现一些处理空值的策略。 这可能会导致呼叫者之间不一致的行为。 在设置的时候,使用任何字段应用某些策略的代码集通常会更好。

有时候,人们希望将可Optional放入集合中,如List<Optional<X>>Map<Key,Optional<Value>> 。 这通常也是一个坏主意。 将这些Optional用法replace为空对象值(不是实际的null引用)通常会更好,或者只是简单地从集合中完全省略这些条目。

很多Serialization相关的问题可以通过将持久化序列化的forms与您所运行的实际运行时实现解耦来解决。

 /** The class you work with in your runtime */ public class My implements Serializable { private static final long serialVersionUID = 1L; Optional<Integer> value = Optional.empty(); public void setValue(Integer i) { this.value = Optional.ofNullable(i); } public Optional<Integer> getValue() { return value; } private Object writeReplace() throws ObjectStreamException { return new MySerialized(this); } } /** The persistent representation which exists in bytestreams only */ final class MySerialized implements Serializable { private final Integer value; MySerialized(My my) { value=my.getValue().orElse(null); } private Object readResolve() throws ObjectStreamException { My my=new My(); my.setValue(value); return my; } } 

Optional实现的行为允许在处理可能不存在的值时编写好的代码(与使用null相比)。 但是它不会为您的数据的持久表示增加任何好处。 这只会让你的序列化数据更大…

上面的草图可能看起来很复杂,但是这是因为它只显示一个属性的模式。 应该揭示你的class级越多的特性越简单。

不要忘记,完全改变My的实现的可能性,而不需要修改持久化的forms。

如果你想要一个可序列化的可选项,可以考虑使用可串行化的guava的可选项 。

这是一个奇怪的遗漏。

您必须将该字段标记为transient并提供自己写的get()结果本身的自定义writeObject()方法,以及通过从stream中读取结果来恢复OptionalreadObject()方法。 不要忘记分别调用defaultWriteObject()defaultReadObject()