DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Introducing Graph Concepts in Java With Eclipse JNoSQL, Part 3: Understanding Janus
  • Introducing Graph Concepts in Java With Eclipse JNoSQL, Part 2: Understanding Neo4j
  • How to Introduce a New API Quickly Using Micronaut
  • Introducing Graph Concepts in Java With Eclipse JNoSQL

Trending

  • Caching 101: Theory, Algorithms, Tools, and Best Practices
  • How to Format Articles for DZone
  • After 9 Years, Microsoft Fulfills This Windows Feature Request
  • Designing a Java Connector for Software Integrations
  1. DZone
  2. Coding
  3. Java
  4. Serializable Java Lambdas

Serializable Java Lambdas

Here's an article that should help with a specific error that might occur when trying to serialize a lambda with Kryo.

By 
Dan Newton user avatar
Dan Newton
·
Mar. 17, 20 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
9.0K Views

Join the DZone community and get the full member experience.

Join For Free

Recently I was presented with the following error when serializing a lambda with Kryo:

Java
 




x


 
1
com.esotericsoftware.kryo.KryoException: 
2
  java.lang.IllegalArgumentException: 
3
    Unable to serialize Java Lambda expression, unless explicitly declared e.g., 
4
    Runnable r = (Runnable & Serializable) () -> System.out.println("Hello world!");


If you do not recognize the (Runnable & Serializable) syntax, don’t worry, it is merely stating that the lambda must implement two types. This is called Type Intersection. Personally, I have never needed to use this myself, so have never really thought about it. Serializable is a bit of a unique interface in this regards, as there is nothing you actually need to implement.

Without making this cast, the lambda will be considered unserializable, which does not make Kryo happy.

You may also enjoy: How and Why to Serialize Lambdas

As someone who doesn’t look at bytecode very often, I find it amazing how big the difference is when adding and extra casting of & Serializable. The examples below demonstrate this. For clarity, I used the following command to generate bytecode from the code snippets:

Java
 




x


 
1
javap -c -p target.classes.dev.lankydan.IntersectionCasting  


Before doing any casting:

Java
 




xxxxxxxxxx
1


 
1
public class IntersectionCasting {
2

          
3
  public static void main(String[] args) {
4
    Function<String, String> function = (message) -> "Kryo please serialize this message '" + message + "'";
5
  }
6
}


The generated bytecode is:

Java
 




xxxxxxxxxx
1
27


 
1
public class dev.lankydan.IntersectionCasting {
2
  public dev.lankydan.IntersectionCasting();
3
    Code:
4
       0: aload_0
5
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
6
       4: return
7

          
8
  public static void main(java.lang.String[]);
9
    Code:
10
       0: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
11
       5: astore_1
12
       6: return
13

          
14
  private static java.lang.String lambda$main$0(java.lang.String);
15
    Code:
16
       0: new           #3                  // class java/lang/StringBuilder
17
       3: dup
18
       4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
19
       7: ldc           #5                  // String Kryo please serialize this message '
20
       9: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21
      12: aload_0
22
      13: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23
      16: ldc           #7                  // String '
24
      18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25
      21: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26
      24: areturn
27
}


After casting:

Java
 




xxxxxxxxxx
1


 
1
public class IntersectionCasting {
2

          
3
  public static void main(String[] args) {
4
    Function<String, String> function =
5
        (Function<String, String> & Serializable) (message) -> "Kryo please serialize this message '" + message + "'";
6
  }
7
}


The bytecode becomes:

Java
 




xxxxxxxxxx
1
90


 
1
public class dev.lankydan.IntersectionCasting {
2
  public dev.lankydan.IntersectionCasting();
3
    Code:
4
       0: aload_0
5
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
6
       4: return
7

          
8
  public static void main(java.lang.String[]);
9
    Code:
10
       0: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
11
       5: checkcast     #3                  // class java/io/Serializable
12
       8: checkcast     #4                  // class java/util/function/Function
13
      11: astore_1
14
      12: return
15

          
16
  private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
17
    Code:
18
       0: aload_0
19
       1: invokevirtual #5                  // Method java/lang/invoke/SerializedLambda.getImplMethodName:()Ljava/lang/String;
20
       4: astore_1
21
       5: iconst_m1
22
       6: istore_2
23
       7: aload_1
24
       8: invokevirtual #6                  // Method java/lang/String.hashCode:()I
25
      11: lookupswitch  { // 1
26
           -1657128837: 28
27
               default: 39
28
          }
29
      28: aload_1
30
      29: ldc           #7                  // String lambda$main$2cf54983$1
31
      31: invokevirtual #8                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
32
      34: ifeq          39
33
      37: iconst_0
34
      38: istore_2
35
      39: iload_2
36
      40: lookupswitch  { // 1
37
                     0: 60
38
               default: 135
39
          }
40
      60: aload_0
41
      61: invokevirtual #9                  // Method java/lang/invoke/SerializedLambda.getImplMethodKind:()I
42
      64: bipush        6
43
      66: if_icmpne     135
44
      69: aload_0
45
      70: invokevirtual #10                 // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceClass:()Ljava/lang/String;
46
      73: ldc           #11                 // String java/util/function/Function
47
      75: invokevirtual #12                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
48
      78: ifeq          135
49
      81: aload_0
50
      82: invokevirtual #13                 // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodName:()Ljava/lang/String;
51
      85: ldc           #14                 // String apply
52
      87: invokevirtual #12                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
53
      90: ifeq          135
54
      93: aload_0
55
      94: invokevirtual #15                 // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodSignature:()Ljava/lang/String;
56
      97: ldc           #16                 // String (Ljava/lang/Object;)Ljava/lang/Object;
57
      99: invokevirtual #12                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
58
     102: ifeq          135
59
     105: aload_0
60
     106: invokevirtual #17                 // Method java/lang/invoke/SerializedLambda.getImplClass:()Ljava/lang/String;
61
     109: ldc           #18                 // String dev/lankydan/IntersectionCasting
62
     111: invokevirtual #12                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
63
     114: ifeq          135
64
     117: aload_0
65
     118: invokevirtual #19                 // Method java/lang/invoke/SerializedLambda.getImplMethodSignature:()Ljava/lang/String;
66
     121: ldc           #20                 // String (Ljava/lang/String;)Ljava/lang/String;
67
     123: invokevirtual #12                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
68
     126: ifeq          135
69
     129: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
70
     134: areturn
71
     135: new           #21                 // class java/lang/IllegalArgumentException
72
     138: dup
73
     139: ldc           #22                 // String Invalid lambda deserialization
74
     141: invokespecial #23                 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
75
     144: athrow
76

          
77
  private static java.lang.String lambda$main$2cf54983$1(java.lang.String);
78
    Code:
79
       0: new           #24                 // class java/lang/StringBuilder
80
       3: dup
81
       4: invokespecial #25                 // Method java/lang/StringBuilder."<init>":()V
82
       7: ldc           #26                 // String Kryo please serialize this message '
83
       9: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
84
      12: aload_0
85
      13: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
86
      16: ldc           #28                 // String '
87
      18: invokevirtual #27                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
88
      21: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
89
      24: areturn
90
}


Now, I don’t really know how to read bytecode, but even I can see that there is a lot more going on in the version with the & Serializable cast.

If you can explain what is going on there, then be my guest and give me a shout.

One thing I can read from the bytecode above, is the references to SerializedLambda which were not there before. This class is used by compilers and libraries to ensure that lambdas deserialize correctly. Making the intersection cast of Function<String, String> & Serializable changes the underlying type of the lambda, allowing a library like Kryo to properly understand how to deserialize lambdas given to it.

Adding this extra casting of & Serializable is one possible solution to allow Kryo to deserialize lambdas. An alternative route involves creating a new interface that extends both the underlying Function type that you need, along with Serializable. This is useful when putting together a library or API for others to consume. Allowing them to focus purely on implementing their code, rather than worrying about providing the correct casting to satisfy the serialization of their lambdas.

You could use an interface like the one below:

Java
 




xxxxxxxxxx
1


 
1
interface SerializableLambda extends Function<String, String>, Serializable {}


This can then be used to replace the casting in the previous example:

Java
 




xxxxxxxxxx
1


 
1
public class IntersectionCasting {
2

          
3
  public static void main(String[] args) {
4
    SerializableLambda function = (message) -> "Kryo please serialize this message '" + message + "'";
5
  }
6

          
7

          
8
  interface SerializableLambda extends Function<String, String>, Serializable {}
9
}


I have added the bytecode generated by this change below:

Java
 




xxxxxxxxxx
1
89


 
1
public class dev.lankydan.IntersectionCasting {
2
  public dev.lankydan.IntersectionCasting();
3
    Code:
4
       0: aload_0
5
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
6
       4: return
7

          
8
  public static void main(java.lang.String[]);
9
    Code:
10
       // NO CASTING                        // Mention of casting is removed and reference to new interface is added
11
       0: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ldev/lankydan/IntersectionCasting$SerializableLambda;
12
       5: astore_1
13
       6: return
14

          
15
  private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
16
    Code:
17
       0: aload_0
18
       1: invokevirtual #3                  // Method java/lang/invoke/SerializedLambda.getImplMethodName:()Ljava/lang/String;
19
       4: astore_1
20
       5: iconst_m1
21
       6: istore_2
22
       7: aload_1
23
       8: invokevirtual #4                  // Method java/lang/String.hashCode:()I
24
      11: lookupswitch  { // 1
25
           -1657128837: 28
26
               default: 39
27
          }
28
      28: aload_1
29
      29: ldc           #5                  // String lambda$main$2cf54983$1
30
      31: invokevirtual #6                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
31
      34: ifeq          39
32
      37: iconst_0
33
      38: istore_2
34
      39: iload_2
35
      40: lookupswitch  { // 1
36
                     0: 60
37
               default: 135
38
          }
39
      60: aload_0
40
      61: invokevirtual #7                  // Method java/lang/invoke/SerializedLambda.getImplMethodKind:()I
41
      64: bipush        6
42
      66: if_icmpne     135
43
      69: aload_0
44
      70: invokevirtual #8                  // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceClass:()Ljava/lang/String;
45
      73: ldc           #9                  // String dev/lankydan/IntersectionCasting$SerializableLambda
46
      75: invokevirtual #10                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
47
      78: ifeq          135
48
      81: aload_0
49
      82: invokevirtual #11                 // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodName:()Ljava/lang/String;
50
      85: ldc           #12                 // String apply
51
      87: invokevirtual #10                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
52
      90: ifeq          135
53
      93: aload_0
54
      94: invokevirtual #13                 // Method java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodSignature:()Ljava/lang/String;
55
      97: ldc           #14                 // String (Ljava/lang/Object;)Ljava/lang/Object;
56
      99: invokevirtual #10                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
57
     102: ifeq          135
58
     105: aload_0
59
     106: invokevirtual #15                 // Method java/lang/invoke/SerializedLambda.getImplClass:()Ljava/lang/String;
60
     109: ldc           #16                 // String dev/lankydan/IntersectionCasting
61
     111: invokevirtual #10                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
62
     114: ifeq          135
63
     117: aload_0
64
     118: invokevirtual #17                 // Method java/lang/invoke/SerializedLambda.getImplMethodSignature:()Ljava/lang/String;
65
     121: ldc           #18                 // String (Ljava/lang/String;)Ljava/lang/String;
66
     123: invokevirtual #10                 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
67
     126: ifeq          135
68
     129: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ldev/lankydan/IntersectionCasting$SerializableLambda;
69
     134: areturn
70
     135: new           #19                 // class java/lang/IllegalArgumentException
71
     138: dup
72
     139: ldc           #20                 // String Invalid lambda deserialization
73
     141: invokespecial #21                 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
74
     144: athrow
75

          
76
  private static java.lang.String lambda$main$2cf54983$1(java.lang.String);
77
    Code:
78
       0: new           #22                 // class java/lang/StringBuilder
79
       3: dup
80
       4: invokespecial #23                 // Method java/lang/StringBuilder."<init>":()V
81
       7: ldc           #24                 // String Kryo please serialize this message '
82
       9: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
83
      12: aload_0
84
      13: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
85
      16: ldc           #26                 // String '
86
      18: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
87
      21: invokevirtual #27                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
88
      24: areturn
89
}


Most of it is the same, with some new references to the SerializableLambda interface and the removal of the original intersection cast.

As mentioned before, this solution is ideal for library and API authors as it allows developers to write code as usual without having to worry about casting (for example, if the library uses Kryo under the hood). Furthermore, since the interface extends Function which is a @FunctionalInterface, developers can nicely write lambdas/functions and don’t even have to mention the interface if passing it directly into another function or constructor. I personally went down this route when designing a new API for Corda. I wanted to provide the most accessible API for developers to use, while still providing an API that works (I can’t let Kryo blow up…).

In conclusion, in this post which lacks a lot of information and is littered with extended snippets of bytecode, you need to take away two things. You can make a Java lambda/function serializable through type intersection, and you can ensure that your own APIs are clean by creating a new interface that extends both your desired function type and Serializable. These are both routes that should be considered when using a serialisation library like Kryo.

If you enjoyed this post or found it helpful (or both) then please feel free to follow me on Twitter at @LankyDanDev and remember to share with anyone else who might find this useful!

Further Reading

Introduction to Java Bytecode



Java (programming language)

Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Introducing Graph Concepts in Java With Eclipse JNoSQL, Part 3: Understanding Janus
  • Introducing Graph Concepts in Java With Eclipse JNoSQL, Part 2: Understanding Neo4j
  • How to Introduce a New API Quickly Using Micronaut
  • Introducing Graph Concepts in Java With Eclipse JNoSQL

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!