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

van*_*chi 101 java serialization optional java-8

Enum类是Serializable,因此使用枚举序列化对象没有问题.另一种情况是class具有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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Stu*_*rks 154

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

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

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

有时人们想要Optional收藏,比如List<Optional<X>>Map<Key,Optional<Value>>.这也是一个坏主意.通常最好以取代这些用法Optional空-对象的值(不是实际null的参考),或者干脆完全忽略从收集这些条目.

  • 有趣的回答,对我而言,设计选择是完全不可接受和误导的.你说"通常有更好的方法来组织数据,而不是在一个字段中存储一个Optional",当然,也许,为什么不呢,但这应该是设计师的选择,而不是语言的选择.这是另一个案例,我非常想念Java中的Scala选项(Scala选项是Serializable,它们遵循Monad的指导原则) (43认同)
  • "当可能缺少返回值时,Optional的主要设计目标是用作函数的返回值." 好吧,似乎你不能将它们用于远程EJB中的返回值.大... (32认同)
  • 干得好斯图尔特.我必须说,如果推理它是一个非常特殊的链,并且设计一个不打算用作实例成员类型的类是一件非同寻常的事情.特别是当Javadoc中的类合同中没有说明时.也许他们应该设计一个注释而不是一个类. (24认同)
  • 我不太明白这里的推理。这篇文章基于这样的想法:库设计者引入类型背后的意图与类型“应该”如何使用相关。我认为情况并非如此。计算机的发明者旨在加速一些算术,并破解一些加密,但我们却用它来进行流体物理模拟,以优化我们汽车的空气动力学。如果我对 Java 的序列化机制(它已经被破坏得很厉害)不感兴趣,为什么我不应该在它为我服务的地方使用可选,比如在字段或参数中? (3认同)
  • 好在我不需要使用可序列化的字段。否则这将是一场灾难 (2认同)

Hol*_*ger 15

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

该类Optional实现的行为允许在处理可能缺少的值时编写好的代码(与使用相比null).但它不会为数据的持久表示添加任何好处.它只会使您的序列化数据更大......

上面的草图可能看起来很复杂,但这是因为它仅使用一个属性演示模式.你的班级拥有的属性越多,它的简洁性就越多.

而且不要忘记,My完全改变执行的可能性,而不需要调整持久形式......

  • +1,但是有更多的字段会有更多的样板来复制它们. (2认同)

Eri*_*ord 11

如果您想要一个可序列化的可选项,请考虑使用可序列化的guava可选项.


Prz*_*wak 5

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

public interface Option<T> extends Value<T>, Serializable { ... }
Run Code Online (Sandbox Code Playgroud)