为什么 java.util.Optional 不是 Serializable,如何序列化具有此类字段的对象

新手上路,请多包涵

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

如何处理这样的类,如何序列化它们?是否可以将此类对象发送到 Remote 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);
        }
    }
}

原文由 vanarchi 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 806
2 个回答

这个答案是对标题“Shouldn’t Optional be Serializable?”中的问题的回应。简短的回答是 Java Lambda (JSR-335) 专家组 考虑并拒绝了它。那个注释, 这个这个 表明 Optional 的主要设计目标是在可能没有返回值时用作函数的返回值。目的是调用者立即检查 Optional 并提取实际值(如果存在)。如果该值不存在,调用者可以替换默认值、抛出异常或应用其他一些策略。这通常是通过将流式方法调用链接到返回 Optional 值的流管道(或其他方法)的末尾来完成的。

它从未打算用于 Optional 以其他方式使用,例如用于 可选方法参数作为字段存储在对象 中。通过扩展,使 Optional 可序列化将使它能够持久存储或通过网络传输,这两者都鼓励使用远远超出其原始设计目标。

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

有时人们想将 Optional 放入集合中,例如 List<Optional<X>>Map<Key,Optional<Value>> 。这通常也是一个坏主意。通常最好将 Optional 的这些用法替换为 空对象 值(不是实际的 null 引用),或者只是从集合中完全省略这些条目。

原文由 Stuart Marks 发布,翻译遵循 CC BY-SA 3.0 许可协议

许多 Serialization 相关问题可以通过将持久序列化形式与您操作的实际运行时实现解耦来解决。

 /** 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 相比)。但它不会为您的数据的持久表示增加任何好处。它只会让你的序列化数据更大……

上面的草图可能看起来很复杂,但那是因为它只演示了具有一个属性的模式。你的类拥有的属性越多,它的简单性就越明显。

不要忘记,完全无需调整持久形式即可完全更改 My 的实现的可能性……

原文由 Holger 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题