람다를 직렬화하는 방법?


157

람다를 우아하게 직렬화하는 방법은 무엇입니까?

예를 들어, 아래 코드는를 던집니다 NotSerializableException. SerializableRunnable"더미"인터페이스를 만들지 않고 어떻게 해결할 수 있습니까?

public static void main(String[] args) throws Exception {
    File file = Files.createTempFile("lambda", "ser").toFile();
    try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(file))) {
        Runnable r = () -> System.out.println("Can I be serialized?");
        oo.writeObject(r);
    }

    try (ObjectInput oi = new ObjectInputStream(new FileInputStream(file))) {
        Runnable  r = (Runnable) oi.readObject();
        r.run();
    }
}

18
이것이 가능하지만 (선택된 답변 참조) 모든 사람은 실제로이 작업에 대해 두 번 생각해야합니다. 공식적으로 "권장하지 말아야하며" 보안에 심각한 영향을 미칠 수 있습니다 .
David

답변:


260

Java 8은 다중 경계를 추가하여 객체를 유형의 교차로 캐스팅 할 수있는 가능성을 소개합니다 . 직렬화의 경우 다음과 같이 쓸 수 있습니다.

Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");

그리고 람다는 자동적으로 직렬화 가능합니다.


4
매우 흥미 롭습니다.이 기능은 매우 강력합니다. 람다 캐스팅 이외의 캐스트 표현을 사용하고 있습니까? 예를 들어 일반 익명 클래스와 비슷한 것을 할 수 있습니까?
Balder

6
@Balder 람다의 유형 유추에 대한 대상 유형을 제공하기 위해 교차 유형으로 캐스트하는 기능이 추가되었습니다. AIC에는 매니페스트 유형 (즉, 해당 유형이 유추되지 않음)이 있으므로 AIC를 교차 유형으로 캐스팅하는 것은 유용하지 않습니다. AIC가 여러 인터페이스를 구현하게하려면 모든 인터페이스를 확장하는 새로운 하위 인터페이스를 만든 다음 인스턴스화해야합니다.
스튜어트 마크

2
serialVersionUID가 정의되어 있지 않다는 컴파일러 경고가 발생합니까?
Kirill Rakhman

11
참고 : 시공 중에 캐스트를 적용한 경우에만 작동합니다. 다음은 ClassCastException을 발생시킵니다. Runnable r = ()-> System.out.println ( "Serializable!"); 실행 가능 직렬화 가능 R = (실행 가능 및 직렬화 가능) r;
bcody


24

메소드 참조에 동일한 구성을 사용할 수 있습니다. 예를 들어이 코드는 다음과 같습니다.

import java.io.Serializable;

public class Test {
    static Object bar(String s) {
        return "make serializable";
    }

    void m () {
        SAM s1 = (SAM & Serializable) Test::bar;
        SAM s2 = (SAM & Serializable) t -> "make serializable";
    }

    interface SAM {
        Object action(String s);
    }
}

직렬화 가능한 대상 유형으로 람다 식과 메소드 참조를 정의합니다.


18

아주 못생긴 캐스트. 사용중인 기능 인터페이스에 대한 직렬화 가능 확장을 정의하는 것을 선호합니다

예를 들면 다음과 같습니다.

interface SerializableFunction<T,R> extends Function<T,R>, Serializable {}
interface SerializableConsumer<T> extends Consumer<T>, Serializable {}

람다를 받아들이는 방법은 다음과 같이 정의 할 수 있습니다.

private void someFunction(SerializableFunction<String, Object> function) {
   ...
}

그리고 함수를 호출하면 추한 캐스트없이 람다를 전달할 수 있습니다.

someFunction(arg -> doXYZ(arg));

2
작성하지 않은 외부 발신자가 자동으로 직렬화 가능하기 때문에이 답변이 마음에 듭니다. 제출 된 객체를 직렬화 할 수있게하려면 인터페이스가 직렬화 될 수있는 인터페이스 여야합니다. 그러나 질문은 " SerializableRunnable'더미'인터페이스 를 만들지 않고 "
slevin

4

Beam / Dataflow 코드를 생성하는 동안 누군가가 여기에 빠지는 경우 :

Beam에는 자체 SerializableFunction Interface가 있으므로 더미 인터페이스 나 자세한 캐스트가 필요하지 않습니다.


3

Kryo 와 같은 다른 직렬화 프레임 워크로 기꺼이 전환하려는 경우 다중 경계 또는 구현 된 인터페이스가 구현해야하는 요구 사항을 제거 할 수 있습니다 Serializable. 접근 방식은

  1. InnerClassLambdaMetafactory직렬화에 필요한 코드를 항상 생성하도록 수정
  2. LambdaMetaFactory역 직렬화 중에 직접 호출

자세한 내용과 코드는이 블로그 게시물을 참조하십시오

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