web-dev-qa-db-ja.com

ラムダをシリアル化する方法は?

ラムダをエレガントにシリアル化するにはどうすればよいですか?

たとえば、次のコードは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();
    }
}
147
assylias

Java 8では、 複数の境界を追加してオブジェクトを型の共通部分にキャストする の可能性が導入されています。したがって、シリアル化の場合、次のように記述できます。

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

そして、ラムダは自動的にシリアライズ可能になります。

246
assylias

同じ構造をメソッド参照に使用できます。たとえば、次のコード:

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

シリアライズ可能なターゲット型を使用してラムダ式とメソッド参照を定義します。

22
Vicente Romero

非常にいキャスト。私は使用している機能的なインターフェースにSerializable拡張を定義することを好みます

例えば:

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));
14
Pascal

Kryo のような別のシリアル化フレームワークに切り替えたい場合、複数の境界や、実装されたインターフェースがSerializableを実装する必要があるという要件を取り除くことができます。アプローチは

  1. InnerClassLambdaMetafactoryを変更して、常にシリアル化に必要なコードを生成します
  2. 逆シリアル化中にLambdaMetaFactoryを直接呼び出す

詳細とコードについては、こちらをご覧ください ブログ投稿

3
ruediste

Beam/Dataflowコードの作成中に誰かがここに落ちた場合:

Beamには独自の SerializableFunction Interfaceがあるため、ダミーのインターフェイスや冗長なキャストは必要ありません。

1
Rafaël