Bagaimana cara membuat serial lambda?

157

Bagaimana saya bisa membuat serial lambda secara elegan?

Misalnya, kode di bawah ini melempar a NotSerializableException. Bagaimana saya bisa memperbaikinya tanpa membuat SerializableRunnableantarmuka "dummy"?

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();
    }
}
assylias
sumber
18
Meskipun ini mungkin (lihat jawaban yang dipilih), semua orang mungkin harus berpikir dua kali untuk benar-benar melakukan ini. Secara resmi "sangat tidak dianjurkan" dan dapat memiliki implikasi keamanan yang serius .
David

Jawaban:

260

Java 8 memperkenalkan kemungkinan untuk melemparkan objek ke persimpangan jenis dengan menambahkan beberapa batas . Dalam hal serialisasi, karena itu dimungkinkan untuk menulis:

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

Dan lambda secara otomatis menjadi serial.

assylias
sumber
4
Sangat menarik - fitur ini sepertinya cukup kuat. Apakah ada penggunaan ekspresi pemeran di luar casting lambdas? Misalnya, apakah sekarang mungkin untuk melakukan sesuatu yang mirip dengan kelas anonim biasa?
Balder
6
@Balder Fasilitas untuk melemparkan ke tipe persimpangan ditambahkan untuk memberikan tipe target untuk inferensi tipe lambdas. Karena AIC memiliki tipe manifes (yaitu, jenisnya tidak disimpulkan) casting AIC ke tipe persimpangan tidak berguna. (Mungkin, tidak berguna.) Untuk memiliki AIC mengimplementasikan beberapa antarmuka, Anda harus membuat subinterface baru yang memanjang semuanya, lalu instantiate.
Stuart Marks
2
Apakah ini menghasilkan peringatan kompiler, mengatakan tidak ada serialVersionUID yang ditentukan?
Kirill Rakhman
11
Catatan: ini hanya berfungsi jika Anda menggunakan gips selama konstruksi. Berikut ini akan membuang ClassCastException: Runnable r = () -> System.out.println ("Serializable!"); Runnable serializableR = (Runnable & Serializable) r;
bcody
3
@bcody Ya lihat juga: stackoverflow.com/questions/25391656/…
assylias
24

Konstruksi yang sama dapat digunakan untuk referensi metode. Misalnya kode ini:

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

mendefinisikan ekspresi lambda dan referensi metode dengan tipe target serial.

Vicente Romero
sumber
18

Pemain yang sangat jelek. Saya lebih suka mendefinisikan ekstensi Serializable ke antarmuka fungsional yang saya gunakan

Sebagai contoh:

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

maka metode menerima lambda dapat didefinisikan sebagai berikut:

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

dan memanggil fungsi Anda dapat melewati lambda Anda tanpa pemain jelek:

someFunction(arg -> doXYZ(arg));
Pascal
sumber
2
Saya suka jawaban ini karena dengan demikian penelepon eksternal yang tidak Anda tulis akan secara otomatis juga serial. Jika Anda ingin objek yang dikirimkan menjadi serializable, antarmuka Anda harus serializable, yang merupakan semacam titik antarmuka. Namun, pertanyaan itu mengatakan "tanpa membuat SerializableRunnableantarmuka 'dummy'"
slevin
4

Jika seseorang jatuh di sini saat membuat kode Beam / Dataflow:

Beam memiliki SerializableFunction Interface sendiri sehingga tidak perlu antarmuka dummy atau gips verbose.

Rafaël
sumber
3

Jika Anda ingin beralih ke kerangka kerja serialisasi lain seperti Kryo , Anda dapat menyingkirkan beberapa batasan atau persyaratan yang harus diterapkan oleh antarmuka yang diimplementasikan Serializable. Pendekatannya adalah untuk

  1. Ubah InnerClassLambdaMetafactoryuntuk selalu menghasilkan kode yang diperlukan untuk serialisasi
  2. Langsung panggil LambdaMetaFactoryselama deserialization

Untuk detail dan kode, lihat posting blog ini

ruediste
sumber