为什么我们不能直接在Lambda函数中调用Thread#sleep()?

Cha*_*ngh 58 java lambda multithreading java-8

下面的代码给我一个编译时错误:

Thread t2 = new Thread(() -> {
    try { 
        sleep(1000);
    } 
    catch (InterruptedException e) {}
});
Run Code Online (Sandbox Code Playgroud)

对于类型A (其中A是我的类名),方法sleep(int)是未定义的.

然而,当我使用匿名内部类时,没有编译时错误:

Thread t1 = new Thread(){
    public void run(){
        try {
            sleep(1000);
        } catch (InterruptedException e) {}
    }
};
Run Code Online (Sandbox Code Playgroud)

以下代码也可以正常工作:

Thread t3 = new Thread(() -> System.out.println("In lambda"));
Run Code Online (Sandbox Code Playgroud)

如何在lambda表达式体内工作?请帮忙.

从许多答案中,我可以看到错误可以Thread.sleep(1000)在我的第一种方法中解决.但是,如果有人能向我解释范围和上下文如何在lambda表达式中工作,我真的很感激.

Swe*_*per 84

Thread.sleep是类中的静态方法Thread.

您可以sleep在匿名类中没有任何限定符的情况下直接调用的原因是因为您实际上是在继承自的类的上下文中Thread.因此,sleep那里是可以访问的.

但是在lambda案例中,你不属于继承自的类Thread.你在这个代码周围的任何类里面.因此,sleep不能直接调用,你需要说Thread.sleep.该文件也支持这一点:

Lambda表达式是词法范围的.这意味着它们不会从超类型继承任何名称或引入新级别的范围.lambda表达式中的声明与封闭环境中的声明一样被解释.

基本上就是说在lambda中,你实际上和你在lambda之外的范围相同.如果你不能进入sleeplambda外面,你也不能在里面.

另请注意,创建此处显示的线程的两种方法本质上是不同的.在lambda中,您将传递RunnableThread构造函数,而在匿名类1中,您Thread通过直接创建它的匿名类来创建它.

  • 你仍然可以使用`import static java.lang.Thread.sleep;`.但是必须捕获`InterruptedException`会产生比前面的`Thread更多的视觉噪声.因此,要创建一个只休眠一秒的新线程,我会使用`new Thread(() - > LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)))`. (4认同)
  • [documentation](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables)说:__ lambda表达式不会引入新的范围.因此,您可以直接访问封闭范围的字段,方法和局部变量._ (3认同)
  • 对我来说,答案的最后一部分与OP的问题最为相关. (3认同)

孙兴斌*_*孙兴斌 18

在第一种方法中,你要传递RunnableThread,你需要打电话Thread.sleep:

Thread t2 = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
});
Run Code Online (Sandbox Code Playgroud)

它是以下的简短版本:

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

Thread t2 = new Thread(runnable);
Run Code Online (Sandbox Code Playgroud)

而在第二,你将覆盖thread.run直接的方法,因此它的确定打电话thread.sleepthread.run.

  • 对我来说,这个答案比其他答案更好.在其他人中,当问题正是你所说的时,我迷失了为什么要谈论匿名课程和lambda. (2认同)

Rya*_*ach 10

这最终成为对Scope的误解.

当您将lambda传递给线程时,您不是在创建Thread的子类,而是传入Runnable的FunctionalInterface 并调用Thread的构造函数.当您尝试调用Sleep时,范围的上下文是Runnable +您的类的组合(如果Runnable接口具有它们,您可以调用默认方法),而不是Thread.

Runnable没有定义sleep(),但Thread没有.

当您创建匿名内部类时,您正在继承Thread,因此您可以调用sleep(),因为Scope的上下文是Thread的子类.

不鼓励使用静态方法,没有类名,因此不鼓励这种误解.使用Thread.Sleep是正确的,并且在所有情况下都是明确的.


Tal*_*dar 7

您的疑问源于对如何定义lambda表达式和匿名类的范围的误解.下面,我将尝试澄清这一点.

Lambda表达式不会引入新的范围.这意味着,在其中,您只能访问在即时封闭的代码块中可以访问的相同内容.看看文档说的内容:

Lambda表达式是词法范围的.这意味着它们不会从超类型继承任何名称或引入新级别的范围.lambda表达式中的声明与封闭环境中的声明一样被解释.

匿名类的工作方式不同.他们确实引入了新的范围.它们的行为很像本地类(在代码块中声明的类),尽管它们不能有构造函数.看看文档说的内容:

像本地类一样,匿名类可以捕获变量; 它们对封闭范围的局部变量具有相同的访问权限:

  • 匿名类可以访问其封闭类的成员.
  • 匿名类无法访问其封闭范围中未声明为final或者有效final的局部变量.
  • 与嵌套类一样,匿名类中的类型(例如变量)的声明会影响封闭范围中具有相同名称的任何其他声明.有关更多信息,请参阅阴影.

在这种情况下,匿名类将在内部充当本地类Thread,因此,它将能够sleep()直接访问,因为此方法将在其范围内.但是,在lambda表达式中,sleep()不会在其范围内(您无法调用sleep()封闭环境),因此必须使用Thread.sleep().请注意,此方法是静态的,因此,不需要其类的实例才能被调用.

  • *怀疑 - >问题.这些不是英语的同义词.(我通常从非母语人士那里看到这一点). (2认同)

S.K*_*.K. 5

以下代码有效:

    Thread t2 = new Thread(() -> {
        try { 
            Thread.sleep(1000);
        } 
        catch (InterruptedException e) {}
    });
Run Code Online (Sandbox Code Playgroud)

这是因为sleep(int milliseconds)是从一个方法Thread类,而你正在创造和传递一个Runnable实例Thread类的构造函数.

在第二种方法中,您正在创建一个类的匿名内部类实例,Thread因此可以访问所有Thread类方法.