Bir lambda nasıl serileştirilir?


157

Bir lambda'yı nasıl zarif bir şekilde serileştirebilirim?

Örneğin, aşağıdaki kod a atar NotSerializableException. SerializableRunnable"Sahte" bir arayüz oluşturmadan nasıl düzeltebilirim ?

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
Bu mümkün olsa da (seçilen cevaba bakınız), herkes muhtemelen bunu iki kez düşünmelidir. Resmi olarak "kesinlikle önerilmez" ve ciddi güvenlik sonuçları olabilir .
David

Yanıtlar:


260

Java 8, birden çok sınır ekleyerek bir nesnenin türlerin kesişme noktasına dökülmesini sağlar . Serileştirme durumunda, aşağıdakileri yazmak mümkündür:

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

Ve lambda otomatik olarak serileştirilebilir hale gelir.


4
Çok ilginç - bu özellik oldukça güçlü görünüyor. Döküm lambdaların dışında böyle bir döküm ifadesinin herhangi bir kullanımı var mı? Örneğin, sıradan bir anonim sınıfa benzer bir şey yapmak şimdi mümkün mü?
Balder

6
@Balder Lambdaların tip çıkarımı için hedef tip sağlamak amacıyla bir kavşak tipine dökülme imkanı eklendi. AIC'lerin bir manifest tipi olduğu için (yani, türü çıkarılmamış) bir AIC'nin kesişim tipine dökülmesi yararlı değildir. (Mümkün, sadece kullanışlı değil.) Bir AIC'nin birden fazla arabirim uygulaması için, hepsini genişleten yeni bir alt arabirim oluşturmanız ve ardından bunu başlatmanız gerekir.
Stuart Marks

2
Bu, serialVersionUID tanımlanmadığını söyleyerek bir derleyici uyarısı üretecek mi?
Kirill Rakhman

11
Not: Bu sadece döküm sırasında inşaat sırasında uygularsanız çalışır. Aşağıdaki bir ClassCastException özel durumu atar: Runnable r = () -> System.out.println ("Serializable!"); Çalıştırılabilir serializableR = (Çalıştırılabilir ve Serileştirilebilir) r;
bcody

3
@bcody Evet ayrıca bakınız: stackoverflow.com/questions/25391656/…
assylias

24

Aynı yapı yöntem referansları için kullanılabilir. Örneğin bu kod:

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

bir lambda ifadesini ve serileştirilebilir bir hedef tipe sahip bir yöntem referansını tanımlar.


18

Çok çirkin döküm. Kullandığım işlevsel arayüze Seri hale getirilebilir bir uzantı tanımlamayı tercih ediyorum

Örneğin:

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

daha sonra lambda'yı kabul eden yöntem şu şekilde tanımlanabilir:

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

ve işlevi çağırarak lambda'nızı çirkin bir döküm olmadan geçirebilirsiniz:

someFunction(arg -> doXYZ(arg));

2
Bu yanıtı beğendim, çünkü o zaman yazmadığınız herhangi bir harici arayan otomatik olarak serileştirilebilecektir. Gönderilen nesnelerin serileştirilebilir olmasını istiyorsanız, arabiriminiz serileştirilebilir olmalıdır, bu da bir arabirimin noktasıdır. Ancak, soru SerializableRunnable"kukla 'bir arayüz oluşturmadan " dedi
slevin

4

Beam / Dataflow kodu oluştururken birinin buraya gelmesi durumunda:

Beam'in kendi SerializableFunction Interface'i vardır, bu nedenle kukla arayüze veya ayrıntılı dökümlere gerek yoktur.


3

Kryo gibi başka bir serileştirme çerçevesine geçmek istiyorsanız, birden fazla sınırdan veya uygulanan arayüzün uygulanması gerekliliğinden kurtulabilirsiniz Serializable. Yaklaşım

  1. InnerClassLambdaMetafactorySerileştirme için gereken kodu her zaman oluşturacak şekilde değiştirin
  2. Deserialization LambdaMetaFactorysırasında doğrudan çağrı

Ayrıntılar ve kod için bu blog gönderisine bakın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.