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

zysjyyx4  于 2021-06-04  发布在  Kafka
关注(0)|答案(6)|浏览(417)

枚举类是可序列化的,因此用枚举序列化对象没有问题。另一种情况是类具有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);
        }
    }
}
ajsxfq5m

ajsxfq5m1#

这个答案是为了回答标题中的问题“optional不应该是可序列化的吗?”简单的回答是javalambda(jsr-335)Maven组考虑并拒绝了它。这一条和这一条表明 Optional 当返回值可能不存在时,将用作函数的返回值。其目的是让调用者立即检查 Optional 并提取实际值(如果存在)。如果该值不存在,调用方可以替换默认值、抛出异常或应用其他策略。这通常是通过链接fluent方法调用返回的流管道(或其他方法)的末尾来完成的 Optional 价值观。
它从来不是为 Optional 以其他方式使用,例如用于可选方法参数或存储为对象中的字段。顺其自然地说 Optional 可串行化将使它能够持久地存储或通过网络传输,这两者都鼓励使用远远超出其最初的设计目标。
通常有更好的方法来组织数据,而不是存储数据 Optional 在田野里。如果一个吸气剂(如 getValue 方法)返回实际值 Optional 从字段开始,它强制每个调用者实现一些处理空值的策略。这可能会导致呼叫者之间的不一致行为。通常最好让该字段的任何代码集在设置时应用某些策略。
有时人们想把 Optional 收藏,比如 List<Optional<X>> 或者 Map<Key,Optional<Value>> . 这通常也是个坏主意。最好是把这些用法替换掉 Optional 对象值为空(不是实际值 null 引用),或者干脆从集合中完全忽略这些条目。

mu0hgdu0

mu0hgdu02#

如果你想要一个可序列化的可选的,可以考虑改用guava的可选的,它是可序列化的。

q3aa0525

q3aa05253#

io库(以前的javaslang)也有 Option 可序列化的类:

public interface Option<T> extends Value<T>, Serializable { ... }
v7pvogib

v7pvogib4#

很多 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 完全不需要调整持久形式…

xxe27gdn

xxe27gdn5#

这是一个奇怪的遗漏。
你必须把这个字段标记为 transient 提供你自己的习惯 writeObject() 编写 get() 结果本身 readObject() 恢复 Optional 从流中读取结果。别忘了打电话 defaultWriteObject() 以及 defaultReadObject() 分别。

iibxawm4

iibxawm46#

如果您想维护一个更一致的类型列表并避免使用null,那么有一个奇怪的替代方法。
可以使用类型的交集存储值。再加上一个lambda,可以实现如下功能:

private final Supplier<Optional<Integer>> suppValue;
....
List<Integer> temp = value
        .map(v -> v.map(Arrays::asList).orElseGet(ArrayList::new))
        .orElse(null);
this.suppValue = (Supplier<Optional<Integer>> & Serializable)() -> temp==null ? Optional.empty() : temp.stream().findFirst();

拥有 temp 变量separate避免关闭 value 成员,因此序列化太多。

相关问题