[java] java.util.Optional이 Serializable이 아닌 이유, 이러한 필드로 객체를 직렬화하는 방법

Enum 클래스는 Serializable이므로 객체를 열거 형으로 직렬화하는 데 문제가 없습니다. 다른 경우는 클래스에 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);
        }
    }
}



답변

이 답변은 “옵션은 직렬화 가능하지 않아야합니까?”라는 제목의 질문에 대한 응답입니다. 짧은 대답은 Java Lambda (JSR-335) 전문가 그룹이이를 고려하고 거부 했다는 입니다. 그 노트, 그리고 이것이것 하나Optional반환 값이 없을 때 함수의 반환 값으로 사용되는 기본 디자인 목표를 나타냅니다 . 의도는 호출자가 즉시 확인 Optional하고 실제 값이있는 경우 추출하는 것입니다. 값이 없으면 호출자는 기본값을 대체하거나 예외를 발생 시키거나 다른 정책을 적용 할 수 있습니다. 이는 일반적으로 Optional값 을 반환하는 스트림 파이프 라인 (또는 다른 메서드)의 끝에서 유연한 메서드 호출을 연결하여 수행됩니다 .

선택적 메서드 인수Optional 와 같은 다른 방법으로 사용 하거나 객체의 필드저장 하기위한 것이 아닙니다 . 확장하면 직렬화가 가능해지면서 지속적으로 저장되거나 네트워크를 통해 전송 될 수 있으며, 두 가지 모두 원래 설계 목표를 훨씬 뛰어 넘는 사용을 장려합니다.Optional

일반적으로 Optional필드에 를 저장하는 것보다 데이터를 구성하는 더 좋은 방법이 있습니다 . getter (예 : getValue질문 의 메서드)가 Optional필드 에서 실제 값 을 반환하면 모든 호출자가 빈 값을 처리하기위한 일부 정책을 구현하도록합니다. 이로 인해 발신자간에 일관성없는 동작이 발생할 수 있습니다. 필드가 설정된 시점에 정책을 적용하는 코드 세트를 갖는 것이 더 나은 경우가 많습니다.

때때로 사람들은 또는 Optional같은 컬렉션 에 넣고 싶어합니다 . 이것도 일반적으로 나쁜 생각입니다. 그것은 이러한 용도로 교체하는 것이 좋습니다 와 널 개체 값 (실제적인 전체 컬렉션에서, 또는 단순히 생략 이러한 항목을 참조).List<Optional<X>>Map<Key,Optional<Value>>Optionalnull


답변

작업하는 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할 것은 영구적 인 형태를 적용 할 필요없이 완전히 구현을 변경할 수 있다는 것입니다 .


답변

직렬화 가능한 옵션을 원한다면 직렬화 가능한 구아바의 옵션 을 대신 사용 하는 것이 좋습니다.


답변

이상한 누락입니다.

필드를로 표시하고 결과 자체 를 작성한 transient사용자 지정 writeObject()메서드 get()와 스트림에서 해당 결과를 읽어를 readObject()복원 하는 메서드를 제공 Optional해야합니다. 전화하는 것을 잊지 defaultWriteObject()않고 defaultReadObject()각각.


답변

Vavr.io 라이브러리 (이전 Javaslang)에는 Option직렬화 가능한 클래스 도 있습니다 .

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


답변

보다 일관된 유형 목록을 유지하고 null을 사용하지 않으려면 한 가지 이상한 대안이 있습니다.

유형의 교차를 사용하여 값을 저장할 수 있습니다 . 람다와 함께 사용하면 다음과 같은 결과를 얻을 수 있습니다.

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구성원 때문에 너무 많은 serialising.


답변