Why can't we call Thread#sleep() directly inside a Lambda function?

JavaMultithreadingLambdaJava 8

Java Problem Overview


The below code is giving me a compile time error:

Thread t2 = new Thread(() -> {
    try { 
        sleep(1000);
    } 
    catch (InterruptedException e) {}
});

> The method sleep(int) is undefined for the type A (where A is my class name).

Whereas, when I use an anonymous inner class, there is no compile time error:

Thread t1 = new Thread(){
    public void run(){
        try {
            sleep(1000);
        } catch (InterruptedException e) {}
    }
};

The below code also works fine:

Thread t3 = new Thread(() -> System.out.println("In lambda"));

How do things work inside a lambda expression body? Please help.

From many answers, I can see that the error can be resolved using Thread.sleep(1000) in my first approach. However, I'd really appreciate if someone could explain to me how scope and context work in a lambda expression.

Java Solutions


Solution 1 - Java

Thread.sleep is a static method in the Thread class.

The reason you can call sleep directly without any qualifiers in an anonymous class is because you are actually in the context of a class that inherits from Thread. Therefore, sleep is accessible there.

But in the lambda case, you are not in a class that inherits from Thread. You are inside whatever class is surrounding that code. Therefore, sleep can't be called directly and you need to say Thread.sleep. The documentation also supports this:

> Lambda expressions are lexically scoped. This means that they do not > inherit any names from a supertype or introduce a new level of > scoping. Declarations in a lambda expression are interpreted just as > they are in the enclosing environment.

Basically that is saying that inside the lambda, you are actually in the same scope as if you were outside of the lambda. If you can't access sleep outside the lambda, you can't on the inside either.

Also, note that the two ways of creating a thread that you have shown here is intrinsically different. In the lambda one, you are passing a Runnable to the Thread constructor, whereas in the anonymous class one, you are creating a Thread by creating an anonymous class of it directly.

Solution 2 - Java

In the first approach, you are passing a Runnable to the Thread, you need call Thread.sleep:

Thread t2 = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
});

it is the short version of:

Runnable runnable = new Runnable() {
    public void run(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
    }
};

Thread t2 = new Thread(runnable);

While in the second, you are overriding thread.run method directly, so it's ok to call thread.sleep in thread.run.

Solution 3 - Java

This ends up being a misunderstanding of Scope.

When you are passing in a lambda to the thread, you are not creating a subclass of Thread, instead you are passing in the FunctionalInterface of Runnable and calling a constructor of Thread. When you attempt to call Sleep, the context of your scope is a combination of Runnable + your class (you can call default methods if the Runnable interface had them), not Thread.

Runnable does not have sleep() defined, but Thread does.

When you create an anonymous inner class, you are subclassing Thread, so sleep() is available for you to call, as the context of the Scope is a subclass of Thread.

Calling static methods, without the class name is discouraged for exactly this kind of misunderstanding. Using Thread.Sleep is both correct, and unambiguous in all circumstances.

Solution 4 - Java

Your doubt originates on a misunderstanding about how the scopes of a lambda expression and an anonymous class are defined. Below, I will try to clarify this.

Lambda expressions DO NOT introduce a new level of scoping. This means that, inside it, you can only access the same things that you would be able to access in the immediately enclosing code block. See what the docs say:

> Lambda expressions are lexically scoped. This means that they do not > inherit any names from a supertype or introduce a new level of > scoping. Declarations in a lambda expression are interpreted just as > they are in the enclosing environment.

Anonymous classes work differently. They do introduce a new level of scoping. They behave much like a local class (a class that you declare inside a block of code), although they can't have constructors. See what the docs say:

>Like local classes, anonymous classes can capture variables; they have the same access to local variables of the enclosing scope: > > - An anonymous class has access to the members of its enclosing class. > - An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final. > - Like a nested class, a declaration of a type (such as a variable) in an anonymous class shadows any other declarations in the enclosing > scope that have the same name. See Shadowing for more information.

In this context, the anonymous class will act like a local class inside Thread and, thus, it will be able to access sleep() directly, since this method will be within its scope. However, in the lambda expression, sleep() won't be within its scope (you can't call sleep() on the enclosing environment), so that you must use Thread.sleep(). Note that this method is static and, therefore, doesn't require an instance of its class in order to be called.

Solution 5 - Java

Following code works:

    Thread t2 = new Thread(() -> {
        try { 
            Thread.sleep(1000);
        } 
        catch (InterruptedException e) {}
    });

This is because sleep(int milliseconds) is a method from Thread class while you are creating and passing a Runnable instance to Thread class constructor.

In the second method, You are creating an anonymous inner class instance of Thread class and thus have access to all Thread class methods.

Solution 6 - Java

I like the answer that was provided and accepted, but in much simpler words, you can think that this has changed from a anonymous inner class to a lambda.

In case of a AIC, this refers to an instance of a class you are extending (in your example that being Thread), in case of lambda expression, this refers to an instance of the class that surrounds the lambda expression (whatever that class is in your example). And I bet in your class where you use the lambda expression, there is not such sleep defined.

Solution 7 - Java

public void foo() {
    new Thread(() -> { sleep(1000); });
}

is equivalent to

public void foo() {
    new Thread(this::lambda$0);
}
private void lambda$0() {
    sleep(1000);
}

so compiler will not lookup sleep in Thread

Solution 8 - Java

Thread.sleep is static method ...

 Thread t2 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
    });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChandraBhan SinghView Question on Stackoverflow
Solution 1 - JavaSweeperView Answer on Stackoverflow
Solution 2 - JavaxingbinView Answer on Stackoverflow
Solution 3 - JavaRyan LeachView Answer on Stackoverflow
Solution 4 - JavaTalendarView Answer on Stackoverflow
Solution 5 - JavaS.K.View Answer on Stackoverflow
Solution 6 - JavaEugeneView Answer on Stackoverflow
Solution 7 - JavayyyyView Answer on Stackoverflow
Solution 8 - JavaniemarView Answer on Stackoverflow