代码之家  ›  专栏  ›  技术社区  ›  ChandraBhan Singh

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

  •  57
  • ChandraBhan Singh  · 技术社区  · 6 年前

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

    类型A的方法sleep(int)未定义 (其中A是我的类名)。

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

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

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

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

    lambda表达式体内部是如何工作的?请帮忙。

    从许多答案中,我可以看出这个错误可以用 Thread.sleep(1000) 在我的第一个方法中。不过,如果有人能向我解释lambda表达式中的范围和上下文是如何工作的,我将不胜感激。

    8 回复  |  直到 6 年前
        1
  •  83
  •   Sweeper    6 年前

    Thread.sleep 是中的静态方法 Thread 班级。

    你打电话的原因 sleep 线程 . 因此,

    但是在lambda的例子中,您不在继承自的类中 线程 睡觉 线程。睡眠 . 这个 documentation 也支持这一点:

    Lambda表达式是词汇范围的。这意味着他们没有 范围界定。lambda表达式中的声明被解释为 它们在封闭的环境中。

    基本上就是说,在lambda内部,你实际上在同一个范围内,就像你在lambda外部一样。如果您无法访问 在外面,你也不能在里面。

    Runnable 线程

        2
  •  18
  •   xingbin    6 年前

    Runnable Thread ,你需要打电话 Thread.sleep

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

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

    在第二种情况下,您将覆盖 thread.run thread.sleep 在里面 线程.运行 .

        3
  •  10
  •   Ryan Leach    6 年前

    这最终是对范围的误解。

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

    Runnable没有定义sleep(),但线程定义了sleep()。

    创建匿名内部类时,您是线程的子类,因此可以调用sleep(),因为作用域的上下文是线程的子类。

        4
  •  7
  •   Community CDub    5 年前

    你的怀疑源于对 范围 定义了一个lambda表达式和一个匿名类。下面,我将试图澄清这一点。

    docs 说:

    Lambda表达式是词汇范围的。这意味着他们没有 范围界定。lambda表达式中的声明被解释为 它们在封闭的环境中。

    匿名类的工作方式不同。它们确实引入了一个新的范围界定级别。他们的行为很像 local class (在代码块中声明的类),尽管它们不能有构造函数。看看这是怎么回事 docs 说:

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

    • 匿名类不能访问其封闭作用域中未声明为final或有效final的局部变量。

    在这种情况下,匿名类的行为就像内部的本地类一样 Thread 因此,它将能够访问 sleep() 直接,因为此方法将在其范围内。但是,在lambda表达式中, 不在其范围内(您不能呼叫 因此您必须使用 Thread.sleep() . 请注意,此方法是 因此,不需要调用其类的实例。

        5
  •  5
  •   S.K.    6 年前

    以下代码工作:

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

    这是因为 sleep(int milliseconds) 是来自的方法 Thread 在创建和传递 Runnable 实例到 线程 类构造函数。

    类,从而可以访问所有 线程 类方法。

        6
  •  4
  •   Eugene    6 年前

    我喜欢被提供和接受的答案,但用更简单的话来说,你可以这样想 this 已从匿名内部类更改为lambda。

    如果是AIC, Thread ),如果 lambda expression , 引用围绕lambda表达式的类的实例(无论该类在示例中是什么)。我敢打赌,在你的课堂上,如果你使用lambda表达式,就不会有这样的结果 sleep 定义。

        7
  •  2
  •   yyyy    6 年前
    public void foo() {
        new Thread(() -> { sleep(1000); });
    }
    

    相当于

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

    所以编译器不会进行查找 sleep Thread

        8
  •  -2
  •   niemar    6 年前

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