9

When lambda expression is used Java actually creates an anonymous (non-static) class. Non-static inner classes always contain references their enclosing objects.

When this lambda expression is called from another library that may invoke lambda in a different process that invocation crashes with class not found exception because it cannot find enclosing object's class in another process.

Consider this example:

public class MyClass {
    public void doSomething() {
        remoteLambdaExecutor.executeLambda(value -> value.equals("test"));
    }
}

Java will create an anonymous inner class that implements certain functional interface and pass it as a parameter to executeLambda(). Then remoteLambdaExecutor will take that anonymous class across the process to run remotely. Remote process knows nothing about MyClass and will throw

java.lang.ClassNotFoundException: MyClass

Because it needs MyClass for that enclosing object reference.

I can always use a static implementation of the functional interface expected by an API, but that defeats the purpose and doesn't utilize lambda functionality.

Is there way to solve it using lambda expressions?

UPDATE: I cannot use static class either unless it's somehow exported to that other process.

ATrubka
  • 3,982
  • 5
  • 33
  • 52

1 Answers1

8

Your initial premise is wrong. The JRE will not generate an anonymous inner class. It may generate a class, but if your lambda expression does not access this or a non-static member of the class, it will not keep a reference to the this instance.

However, this does not imply that the class itself is unnecessary. Since the class hosts the code of the lambda expression, it will always be needed. In this regard, your solution of using a static nested class doesn’t change anything about it, as then, it’s the static nested class that is needed for the execution of the code.

There is no way to transfer an object to a remote execution facility without transferring the class that contains the code to execute (unless the class already exists at the remote site).

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 2
    In other words, a Java lambda is more like a C++ function pointer than a Lisp lambda. The Java lambda tells you what code to run, but does not actually contain that code. I had not realized this. – Troy Daniels Feb 25 '16 at 19:45
  • Otherwise access to variables of the surrounding scope would also not be possible, see https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables – Arne Burmeister Feb 25 '16 at 20:08
  • Holger, you're correct. My assumptions were wrong. The question still stands. Is there a way to do it? Maybe there is? – ATrubka Feb 25 '16 at 20:24
  • 1
    Well, you have to transfer the *classes* to the other site. Since class files are just a bunch of bytes, this is possible in general. In the case of lambda expressions, don't focus on the generated classes—these classes can be regenerated on the other side (that's how serializable lambdas work). You only have to transfer the defining class. But it's easier if you just let both processes access the stored classes in the first place. – Holger Feb 25 '16 at 21:13
  • Holger, unfortunately, it's not always in our control as it affects entire cluster of Hazelcast machines or some other distributed network. If I were to use Groovy script I could do the trick as long as groovy-all.jar is loaded. It can compile classes on the fly. Apparently, lambdas are lacking this... – ATrubka Feb 26 '16 at 20:33
  • 1
    Lambda expressions are not meant to change the nature of the Java language being a compiled language, not a scripting language. If you have the compiler API available at the remote side, you could send source code and compile it at the other side, however, that’s not a smaller effort than sending the byte code of classes to the other side. •  So the main difference is not a technical issue, but the fact that the maintainer of the distributed network got convinced to deploy `groovy-all.jar` but not to deploy a library for the distribution of Java code (or your application’s code) – Holger Feb 29 '16 at 17:02
  • Holger, the difference between groovy-all.jar and a custom developed set of classes is that the former is the only thing you ever need to deploy whle with custom classes you would have to find a way to constantly propagate new changes to every process in your system that needs to run the said lambdas. Since there are no more good answers in a year I mark yours best. – ATrubka Jan 12 '18 at 17:40
  • @Holger So, if I understood you correctly - if lambda expression uses either this or any object from enclosing class then in this case it basically acts as anonymous class ? – starwarrior8809 Nov 26 '20 at 19:36
  • 1
    @starwarrior8809 “basically acts as anonymous class” is an ambiguous term. The classes generated for lambda expressions always share some traits with anonymous inner classes, but even in cases where they capture `this`, there still are lots of differences. Maybe [this answer](https://stackoverflow.com/a/47408390/2711488) sheds some light on it. – Holger Nov 27 '20 at 12:32