Java 8 Lambda in details part II : Scoping of “this” and “effectively final” variable semantic

In this second post, we’ll look at the scoping of the this keyword inside a lambda expression and the semantic of the so called “effectively final” local variables.

DISCLAIMER: all the details exposed in this post were observed related to the JDK 8 demo version as of July 10th 2012. Since the JDK is still in beta, some assertions may not hold in future

Please note that all the example code in this post can be found on my GitHub repository https://github.com/doanduyhai/Java8_Lambda_In_Details

 

I Scoping of “this”

First, as a reminder, below are the principal variable scopes in Java

  1. class level scope: the variable is accessible anywhere in the class, either statically or dynamically at class instance creation
  2. Method scope: the variable is accessible only in the declaring method
  3. Code block scope: the variable is accessible only in the declaring code block. A code block is a portion of code enclosed in braces {…}. Loops like for, do, while as well as static and synchronized blocks are considered code block

Unlike anonymous functions, a lambda expression can be considered as a simple code block with regarde to variable scoping. Consequently all scoping rules for code blocks also apply to lambda expression.

Let’s look at the below code:

public class MutableObject
{
	private String content = "default_content";
	...
}
public class ScopingOfThis
{
	final public MutableObject mutable = new MutableObject();

	public VariableCaptureInClosureSAM createClosure()
	{
		VariableCaptureInClosureSAM lambda = ()->
		{		
			MutableObject mutable = new MutableObject();
			mutable.setContent("from_closure");
	
			return this.mutable;
		};

		return lambda;
	}

	public VariableCaptureInClosureSAM createAnonymous()
	{
		VariableCaptureInClosureSAM anonymousClass = new VariableCaptureInClosureSAM()
		{
			MutableObject mutable = new MutableObject();

			@Override
			public MutableObject retrieveMutable()
			{
				this.mutable.setContent("from_anonymous");
				return this.mutable;
			}

		};

		return anonymousClass;

	}

	public static void main(String[] args)
	{
		ScopingOfThis scopingOfThis = new ScopingOfThis();
		VariableCaptureInClosureSAM sam = scopingOfThis.createClosure();

		MutableObject mutableFromClosure = sam.retrieveMutable();
		System.out.println("nmutableFromClosure content = " + mutableFromClosure.getContent());

		MutableObject mutableFromAnonymous = scopingOfThis.createAnonymous().retrieveMutable();
		System.out.println("mutableFromAnonymous content = " + mutableFromAnonymous.getContent());
		System.out.println("");
	}
}

At line 17 in the lambda expression, the this keyword is used to distinguish the class level mutable field from the local mutable (“from_closure”) field.

At line 33 in the anonymous class, the this.mutable refers to the mutable field declared in the anonymous class only (line 27)

Not surprisingly, the output gives

mutableFromClosure content = default_content
mutableFromAnonymous content = from_anonymous

Now, what if we declare a local mutable field in the createClosure() method before creating the lambda expression ?

	public VariableCaptureInClosureSAM createClosure()
	{	
		MutableObject mutable = new MutableObject();
		
		VariableCaptureInClosureSAM lambda = ()->
		{	
			MutableObject mutable = new MutableObject();
			mutable.setContent("from_closure");
			
			return this.mutable;
		};

		return lambda;
	}

The compilation will fail with the error

ScopingOfThis.java:16: error: variable mutable is already defined in method createClosure()
        MutableObject mutable = new MutableObject();
                                   ^

No surprise here. As in a normal code block you cannot declare a local variable with the same name (the type doesn’t matter) if it already exists in the current lexical scope.

The above code example can be found on GitHub at https://github.com/doanduyhai/Java8_Lambda_In_Details. Just execute the ScopingOfThis.bat(ScopingOfThis.sh) script

 

II “Effectively final” semantic for local variables

In the JDK 7, variable from enclosing class can be used in anonymous functions only if they are declared final. With the JDK 8 this constraint has been relaxed both for lambda expressions and inner classes. It is so called “effectively final” if the variable initial value does not change. But what is the initial value ? It is its reference ? Is it its state ? The litterature is very vague about the fact (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.htm chapter 7)

The best way to clarify it is testing.

public class LocalEffectivelyFinalVariable
{
	public VariableShadowingSAM retrieveVariableShadowingSAM()
	{
		List<String> textList = new ArrayList<String>();
		textList.add("default");
		VariableShadowingSAM sam = () ->
		{
			return textList.get(0);
		};

		textList.clear();
		textList.add("changed_default");
		return sam;
	}

	public static void main(String... args)
	{
		LocalEffectivelyFinalVariable variableShadowing = new LocalEffectivelyFinalVariable();
		String text = variableShadowing.retrieveVariableShadowingSAM().process();
		System.out.println("nnttext = " + text);
		System.out.println("");

	}
}

In the above code, after initializing the list with “default” (line 6), the lambda expression is created and returns the first element of this list (line 9).

Right after lambda declaration, the list is cleared and initialized to “changed_default” (lines 12-13).

How is the local “effectively finaltextList variable captured ? 2 implementations are possible:

  1. either the lambda catures the reference on the returned object, which is the “default” string (testList.get(0)). It means that any subsequent change in the textList would not affect the returned value at runtime
  2. or the lambda only captures the reference on the textList variable and delays get(0) invocation at runtime. This second implementation makes the returned value unpredictable since the textList can be changed after lambda definition

The output displays:

text = changed_default

This result clearly demonstrates that the second implementation has been chosen for JDK 8. The lambda only keeps the reference on local variable and method invocation is performed at runtime !

Now what if we change the reference on the textList variable after lambda expression creation ?

	public VariableShadowingSAM retrieveVariableShadowingSAM()
	{
		List<String> textList = new ArrayList<String>();
		textList.add("default");
		VariableShadowingSAM sam = () ->
		{
			return textList.get(0);
		};
		textList = new ArrayList<String>();
		textList.add("new_instance");

		return sam;
	}

The output displays:

text = default

This result confirms our previous analysis. The lambda keeps the reference on the old textList instance. Even though the local textList has been assigned a new instance of ArrayList with a “new_instance” value, the lambda execution at runtime still displays “default“.

This is the true meaning of “effectively final”. The lambda capture the reference of your local variable in its declaration. No matter how the variable is assigned a new reference afterward, the old reference is still hold by the lambda. It is as if the variable was declared final in the end.

It is interesting to compare JDK 8 closure implementation with Javascript implementation. If you’re familiar with Javascript internals you should know that a closure in Javascript keeps reference to the local variables through scope chain.

function getClosure()
{
    var text="default";

    var closure = function()
    {
        return text;
    }

    text="changed_default";

    return closure;
}

>getClosure()();

>”changed_default”

At runtime, when the closure is invoked, it will look for the text variable in its scope chain. Since the getClosure function (object) is in the chain, the text reference is returned. The displayed value corresponds to the last known value for text, which was “changed_default“.

It is very similar to the capture of the this reference by lambda expression in Java discussed in the previous article.

The above code example can be found on GitHub at https://github.com/doanduyhai/Java8_Lambda_In_Details. Just execute the LocalEffectivelyFinalVariable.bat(LocalEffectivelyFinalVariable.sh) script

To be continued …
 
 

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.