为什么java.util.Optional是不可序列化的,如何使用这样的字段序列化对象

2022-08-31 08:47:36

Enum 类是可序列化的,因此使用枚举序列化对象没有问题。另一种情况是类具有 java.util.Optional 类的字段。在这种情况下,将引发以下异常: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);
        }
    }
}

答案 1

这个答案是对标题中的问题的回应,“可选不应该是可序列化的吗?简短的回答是Java Lambda(JSR-335)专家组考虑并拒绝了它。该注释,以及此注释和此注释表明,当返回值可能不存在时,主要设计目标是用作函数的返回值。目的是调用方立即检查并提取实际值(如果存在)。如果该值不存在,则调用方可以替换默认值、引发异常或应用其他策略。这通常是通过将 fluent 方法调用从返回值的流管道(或其他方法)的末尾链接出来来完成的。OptionalOptionalOptional

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

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

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


答案 2

通过将持久性序列化形式与实际操作的运行时实现分离,可以解决许多相关问题。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;
    }
}

该类实现了一些行为,允许在处理可能缺少的值时编写良好的代码(与使用 相比)。但它不会为数据的持久表示形式增加任何好处。它只会使您的序列化数据更大...Optionalnull

上面的草图可能看起来很复杂,但这是因为它仅使用一个属性演示了模式。类具有的属性越多,其简单性就越应该显示出来。

而且不要忘记,完全可以改变实现,而不需要调整持久形式......My