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


107

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

경우 Optional로 표시 한 Serializable경우, 다음 무슨 일이 일어날 것 get()직렬화 할 수없는 무언가를 반환?
WW.

10
@W W. NotSerializableException,물론 얻을 것 입니다.
론의 후작

7
@W W. 컬렉션과 같습니다. 대부분의 컬렉션 클래스는 직렬화 가능하지만 컬렉션에 포함 된 모든 개체도 직렬화 할 수있는 경우에만 실제로 컬렉션 인스턴스를 직렬화 할 수 있습니다.
Stuart Marks

내 개인적인 견해는 사람들에게 객체의 직렬화조차도 단위 테스트를 적절하게 상기시키는 것이 아닙니다. 그냥 우연히 java.io.NotSerializableException (java.util.Optional) 자신 ;-(
GhostCat

답변:


171

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

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

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

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


26
잘 했어 스튜어트. 추론하는 경우 비범 한 체인이라고 말해야하며 인스턴스 멤버 유형으로 사용하도록 의도되지 않은 클래스를 디자인하는 데는 비범 한 일입니다. 특히 Javadoc의 클래스 계약에 명시되지 않은 경우. 아마도 그들은 클래스 대신 주석을 디자인했을 것입니다.
Marquis of Lorne

1
이 Sutart의 대답 뒤에있는 근거에 우수 블로그 게시물입니다 : blog.joda.org/2014/11/optional-in-java-se-8.html
웨슬리 하트 포드

2
직렬화 가능한 필드를 사용할 필요가 없습니다. 이것은 그렇지 않으면 재앙이 될 것입니다
Kurru

48
흥미로운 대답은 디자인 선택이 완전히 받아 들일 수없고 오도 된 것이 었습니다. "보통 필드에 옵션을 저장하는 것보다 데이터를 구성하는 더 좋은 방법이 있습니다."라고 말합니다. 물론 그렇지 않을 수도 있지만 언어가 아닌 디자이너의 선택이어야합니다. Java에서 Scala 옵션이 너무 그리워지는 또 다른 경우입니다 (Scala 옵션은 Serializable이며 Monad의 지침을 따릅니다)
Guillaume

37
"Optional의 기본 설계 목표는 반환 값이 없을 때 함수의 반환 값으로 사용하는 것입니다." 음, 원격 EJB의 반환 값에 사용할 수없는 것 같습니다. 위대한 ...
틸로

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

이 클래스 Optional는 ( 를 사용하는 것과 비교하여) 없을 가능성이있는 값을 처리 할 때 좋은 코드를 작성할 수있는 동작 을 구현 null합니다. 그러나 데이터의 지속적인 표현에 어떤 이점도 추가하지 않습니다. 직렬화 된 데이터를 더 크게 만들뿐입니다.

위의 스케치는 복잡해 보일 수 있지만 하나의 속성으로 만 패턴을 보여주기 때문입니다. 클래스에 속성이 많을수록 단순성이 더 많이 드러납니다.

그리고 잊지 말아야 My할 것은 영구적 인 형태를 적용 할 필요없이 완전히 구현을 변경할 수 있다는 것입니다 .


2
+1하지만 필드가 많을수록 복사 할 상용구가 더 많아집니다.
Marko Topolnik 2014

1
@Marko Topolnik : 속성 및 방향 당 최대 한 줄. 그러나에 대한 적절한 생성자를 제공 class My하면 일반적으로 다른 용도로도 편리 readResolve하므로 한 줄로 구현할 수 있으므로 상용구를 속성 당 한 줄로 줄일 수 있습니다. My어쨌든 각 변경 가능한 속성이 클래스에 최소한 7 줄의 코드를 가지고 있다는 사실을 감안할 때 그다지 많지 않습니다 .
Holger

나는 같은 주제를 다루는 글을 썼다. 본질적으로이 답변의 긴 텍스트 버전입니다. Serialize Optional
Nicolai

단점은 옵션을 사용하는 모든 bean / pojo에 대해 수행해야한다는 것입니다. 그러나 여전히 좋은 생각입니다.
GhostCat


4

이상한 누락입니다.

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


내가 클래스의 코드를 소유하고 있다면 단순한 객체를 필드에 저장하는 것이 더 편리합니다. 이러한 경우 선택적 클래스는 클래스의 인터페이스에 대해 제한됩니다 (get 메서드는 Optional.ofNullable (field)를 반환 함). 그러나 내부 표현의 경우 Optional을 사용하여 해당 값이 선택 사항임을 명확하게 표시하는 것은 불가능합니다.
vanarchi

난 그냥 것을 보여준 것입니다 수 있습니다. 어떤 이유로 다르게 생각한다면 정확히 무엇에 대한 질문입니까?
론의 후작

답변 해 주셔서 감사합니다. 옵션이 추가됩니다. 내 의견에서 나는이 주제에 대한 더 많은 생각을 보여주고 싶었습니다. 각 솔루션의 Pro와 대조를 고려하십시오. 사용법 writeObject / readObject 메소드에서 우리는 상태 표현에서 Optional의 명확한 의도를 가지고 있지만 직렬화의 구현은 더 복잡해집니다. 계산 / 스트림에서 필드가 집중적으로 사용되는 경우-writeObject / readObject를 사용하는 것이 더 편리합니다.
vanarchi

댓글은 아래에 나타나는 답변과 관련이 있어야합니다. 당신의 생각의 관련성은 솔직히 저를 피합니다.
Marquis of Lorne

3

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

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

0

보다 일관된 유형 목록을 유지하고 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.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.