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

Enum 类是可序列化的,因此使用枚举序列化对象没有问题。另一种情况是 class 具有 java.util 字段。可选课程。在这种情况下,将引发以下异常: java.io。异常: java.util。可以选择

如何处理这样的类,如何序列化它们?是否有可能将这些对象发送到远程 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);
}
}
}
45502 次浏览

It's a curious omission.

You would have to mark the field as transient and provide your own custom writeObject() method that wrote the get() result itself, and a readObject() method that restored the Optional by reading that result from the stream. Not forgetting to call defaultWriteObject() and defaultReadObject() respectively.

许多与 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实现的可能性,不需要任何形式的持久性调整..。

这个答案是对标题中的问题的回应,“可选项不应该是可序列化的吗?”简短的回答是 JavaLambda (JSR-335)专家组 考虑并拒绝了它。该说明以及 这个这个表明,Optional的主要设计目标是在可能没有返回值的情况下用作函数的返回值。这样做的目的是让调用者立即检查 Optional,并在实际值出现时提取它。如果该值不存在,调用方可以替换默认值、引发异常或应用其他策略。这通常是通过链接返回 Optional值的流管道(或其他方法)末端的连贯方法调用来完成的。

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

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

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

如果您想要可序列化的可选项,可以考虑使用可序列化的 番石榴是可选的

Io 库(前 Javaslang)也有可序列化的 Option类:

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

如果您想维护一个更一致的类型列表并避免使用 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变量分开可以避免关闭 value成员的所有者,从而避免序列化过多。

the problem is you have used variables with optional. the basic solution to avoid this, provide the variable without optional and get them as optional when you call the getter like below. Optional<Integer> value = Optional.empty(); to Integer value = null;

public class My implements Serializable {


private static final long serialVersionUID = 1L;
//Optional<Integer> value = Optional.empty(); //old code
Integer value = null; //solution code without optional.


public void setValue(Integer value ) {
//this.value  = Optional.of(value); //old code with Optional
this.value  = value ; //solution code without optional.
}


        

public Optional<Integer> getValue() {
//solution code - return the value by using Optional.
return Optional.ofNullable(value);
}
}

Just copy Optional class to your project and create your own custom Optional that implements Serializable. I am doing it because I just realized this sh*t too late.