Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the exact difference between Java 8 Lambda constructs and JavaScript? [closed]

I have to convert Java 8 code to JavaScript (one way, once in a lifetime). To speed things up I want to automate as much as possible and use the test suite afterwards to fix all remaining issues.

I wonder what the difference between Java 8 lambdas and JavaScript (functions) are?

Any important incompatibilities?

like image 486
Martin Kersten Avatar asked Jan 26 '16 10:01

Martin Kersten


People also ask

What is the difference between closure and lambda?

Lambda functions may be implemented as closures, but they are not closures themselves. This really depends on the context in which you use your application and the environment. When you are creating a lambda function that uses non-local variables, it must be implemented as a closure.

Which of these statements are true for lambda expressions in Java 8?

Q 5 - Which of the following is correct about Java 8 lambda expression? A - Lambda expressions are used primarily to define inline implementation of a functional interface.

What is difference between lambda expression and method in Java?

Core Java bootcamp program with Hands on practice Lambda expression is an anonymous method (method without a name) that has used to provide the inline implementation of a method defined by the functional interface while a method reference is similar to a lambda expression that refers a method without executing it.

What is closure lambda?

A lambda closure is created when a lambda expression references the variables of an enclosing scope (global or local). The rules for doing this are the same as those for inline methods and anonymous classes. Local variables from an enclosing scope that are used within a lambda have to be final .


2 Answers

One important thing to note when comparing Java lambdas and JS functions is how the two treat scoping.

In Java, lambdas can only access effectively final variables and only captures those that are explicitly required. In JS, functions can access all variables in all the parent closures and thus captures everything.

A result of this is the possibility of memory leaks unless you know what you're doing. As an example:

IntSupplier getSupplier(MyMemoryHeavyClass heavy) {
    int x = heavy.hashCode();
    return () -> x;
}

This method will return a lambda that effectively only contains an int. No problems here. However, a naive translation to JavaScript...

function getSupplier(heavy) {
    var x = heavy.hashCode();
    return function() { return x; };
}

It may not be obvious at a glance, but this has a major problem. The function expression will capture EVERYTHING within the scope, including the heavy parameter (even though it's not referenced from within the returned function). As a result, it prevents heavy (which in this example requires a large amount of memory) from being garbage collected and will thus remain in memory for as long as the returned function exists.

EDIT

As pointed out in the comments, this information may be a bit out of date as modern engines appear to be more intelligent. V8, for instance, will apparently only capture whatever it deems necessary. However, it can still be tricked as all functions created in the same scope appear to share the same closure.

For instance, adding the line (function y() { return heavy; });, which would otherwise do pretty much nothing, will force heavy into the same closure as the return x; function uses, creating the leak.

While far-fetched in this specific example, it's far from unthinkable that similar problems may occur when naively translating Java methods containing multiple lambdas.

like image 118
BambooleanLogic Avatar answered Sep 29 '22 11:09

BambooleanLogic


One major difference between JavaScript functions/arrow function expressions and Java 8 anonymous classes/lambdas is that the latter (Java) can't reassign captured variables and parameters as they are considered effectively final in Java 8 (in previous Java versions all captured variables and parameters must be declared final). There are some workarounds to bypass this limitation:

  • Use a custom Mutable<T> with set and get methods as a Mutable<?> instance might be passed as a captured variable or parameter.
  • Use, let's say, AtomicReference<T> or some similar Atomic*** methods (if I'm right, this approach cannot be used in GWT).
  • Use a single element array as a value box, as array elements can be reassigned easily, let's say Object[] o = {null}. Despite this workaround seems to be more low-level and it requires careful reassignment/referencing via indexes, it may have the following advantages: 1) It deals with primitive types perfectly (say, new int[1] is definitely better than Mutable<Integer>). 2) An array can hold more than one value (let's say, your lambda expression might want to modify captured r, g, and b and the triple could be wrapped in float[] rgb = new float[3] where rgb[0] could be r). This workaround, if I'm right, is a suggested intention in IntelliJ IDEA.

So, if you have code with workarounds above in your Java code, you can try to get rid of those workarounds while porting to JavaScript. Let's say, the following Java code with primitive array box

final float rgb[] = new float[3];
final Runnable whiteOut = () -> {
    rgb[0] = 255;
    rgb[1] = 255;
    rgb[2] = 255;
};
final Runnable blackOut = new Runnable() {
    @Override
    public void run() {
        rgb[0] = 255;
        rgb[1] = 255;
        rgb[2] = 255;
    }
};
whiteOut.run();
blackOut.run();

can be easily translated to cleaner JavaScript (if you're fine with separate r, g, and b):

var r, g, b;
var whiteOut = () => { r = 255; g = 255; b = 255; }
var blackOut = function() { r = 0; g = 0; b = 0; }

whiteOut();
blackOut()
like image 41
Lyubomyr Shaydariv Avatar answered Sep 29 '22 11:09

Lyubomyr Shaydariv