Why java.util.Optional is not Serializable, how to serialize the object with such fields

JavaSerializationJava 8Optional

Java Problem Overview


The Enum class is Serializable so there is no problem to serialize object with enums. The other case is where class has fields of java.util.Optional class. In this case the following exception is thrown: java.io.NotSerializableException: java.util.Optional

How to deal with such classes, how to serialize them? Is it possible to send such objects to Remote EJB or through RMI?

This is the example:

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);
        }
    }
}

Java Solutions


Solution 1 - Java

This answer is in response to the question in the title, "Shouldn't Optional be Serializable?" The short answer is that the Java Lambda (JSR-335) expert group considered and rejected it. That note, and this one and this one indicate that the primary design goal for Optional is to be used as the return value of functions when a return value might be absent. The intent is that the caller immediately check the Optional and extract the actual value if it's present. If the value is absent, the caller can substitute a default value, throw an exception, or apply some other policy. This is typically done by chaining fluent method calls off the end of a stream pipeline (or other methods) that return Optional values.

It was never intended for Optional to be used other ways, such as for optional method arguments or to be stored as a field in an object. And by extension, making Optional serializable would enable it to be stored persistently or transmitted across a network, both of which encourage uses far beyond its original design goal.

Usually there are better ways to organize the data than to store an Optional in a field. If a getter (such as the getValue method in the question) returns the actual Optional from the field, it forces every caller to implement some policy for dealing with an empty value. This will likely lead to inconsisent behavior across callers. It's often better to have whatever code sets that field apply some policy at the time it's set.

Sometimes people want to put Optional into collections, like List<Optional<X>> or Map<Key,Optional<Value>>. This too is usually a bad idea. It's often better to replace these usages of Optional with Null-Object values (not actual null references), or simply to omit these entries from the collection entirely.

Solution 2 - Java

A lot of Serialization related problems can be solved by decoupling the persistent serialized form from the actual runtime implementation you operate on.

/** 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;
    }
}

The class Optional implements behavior which allows to write good code when dealing with possibly absent values (compared to the use of null). But it does not add any benefit to a persistent representation of your data. It would just make your serialized data bigger…

The sketch above might look complicated but that’s because it demonstrates the pattern with one property only. The more properties your class has the more its simplicity should be revealed.

And not to forget, the possibility to change the implementation of My completely without any need to adapt the persistent form…

Solution 3 - Java

If you would like a serializable optional, consider instead using guava's optional which is serializable.

Solution 4 - Java

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.

Solution 5 - Java

The Vavr.io library (former Javaslang) also have the Option class which is serializable:

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

Solution 6 - Java

If you want to maintain a more consistent type list and avoid using null there's one kooky alternative.

You can store the value using an intersection of types. Coupled with a lambda, this allows something like:

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();

Having the temp variable separate avoids closing over the owner of the value member and thus serialising too much.

Solution 7 - Java

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);
        }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvanarchiView Question on Stackoverflow
Solution 1 - JavaStuart MarksView Answer on Stackoverflow
Solution 2 - JavaHolgerView Answer on Stackoverflow
Solution 3 - JavaEric HartfordView Answer on Stackoverflow
Solution 4 - Javauser207421View Answer on Stackoverflow
Solution 5 - JavaPrzemek NowakView Answer on Stackoverflow
Solution 6 - JavaDan GravellView Answer on Stackoverflow
Solution 7 - JavaMafeiView Answer on Stackoverflow