developer tip

Lambda 함수 내에서 직접 Thread # sleep ()을 호출 할 수없는 이유는 무엇입니까?

optionbox 2020. 12. 15. 19:02
반응형

Lambda 함수 내에서 직접 Thread # sleep ()을 호출 할 수없는 이유는 무엇입니까?


아래 코드는 나에게 컴파일 시간 오류를 제공합니다.

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

sleep (int) 메소드는 유형 A에 대해 정의되지 않았습니다 (여기서 A는 내 클래스 이름입니다).

반면 익명의 내부 클래스를 사용하면 컴파일 시간 오류가 없습니다.

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

아래 코드도 잘 작동합니다.

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

람다 식 본문 내에서 어떻게 작동합니까? 도와주세요.

많은 답변 Thread.sleep(1000)에서 첫 번째 접근 방식을 사용하여 오류를 해결할 수 있음을 알 수 있습니다 . 그러나 누군가가 람다 식에서 범위와 컨텍스트가 작동하는 방식을 설명해 주시면 정말 감사하겠습니다.


Thread.sleepThread클래스 의 정적 메서드입니다 .

sleep익명 클래스에서 한정자없이 직접 호출 할 수있는 이유 는 실제로에서 상속하는 클래스의 컨텍스트에 있기 때문입니다 Thread. 따라서 sleep거기에 액세스 할 수 있습니다.

그러나 람다의 경우에서 상속하는 클래스에 있지 않습니다 Thread. 당신은 그 코드를 둘러싼 클래스 안에 있습니다. 따라서 sleep직접 전화를 걸 수 없으며 Thread.sleep. 문서는 이 지원합니다

Lambda 표현식은 어휘 적으로 범위가 지정됩니다. 이는 상위 유형에서 이름을 상속하지 않거나 새로운 수준의 범위 지정을 도입하지 않음을 의미합니다. 람다 식의 선언은 둘러싸는 환경에서와 마찬가지로 해석됩니다.

기본적으로 이는 람다 내부에서 마치 람다 외부에있는 것처럼 실제로 동일한 범위에 있음을 의미합니다. sleep람다 외부에서 접근 할 수 없다면 내부에서도 접근 할 수 없습니다.

또한 여기에 표시된 스레드를 만드는 두 가지 방법은 본질적으로 다릅니다. 람다에서는 a RunnableThread생성자에 전달하는 반면 익명 클래스 Thread에서는 익명 클래스를 직접 만들어 생성합니다.


첫 번째 방법에서, 당신이 전달하는 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에서 thread.run.


이것은 Scope의 오해로 끝납니다.

스레드에 람다를 전달할 때 Thread의 하위 클래스를 만드는 것이 아니라 Runnable 의 FunctionalInterface를 전달하고 Thread의 생성자를 호출하는 것입니다. Sleep 호출을 시도 할 때 범위의 컨텍스트는 Thread가 아니라 Runnable + 클래스 (Runnable 인터페이스에 기본 메서드가있는 경우 기본 메서드를 호출 할 수 있음)의 조합입니다.

Runnable에는 sleep ()이 정의되어 있지 않지만 Thread는 정의되어 있습니다.

익명의 내부 클래스를 만들 때 Thread를 서브 클래 싱하므로 Scope의 컨텍스트가 Thread의 서브 클래스이기 때문에 sleep ()을 호출 할 수 있습니다.

클래스 이름없이 정적 메서드를 호출하는 것은 정확히 이런 종류의 오해로 인해 권장되지 않습니다. Thread.Sleep 사용은 모든 상황에서 정확하고 모호하지 않습니다.


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.


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.


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.


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


Thread.sleep is static method ...

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

ReferenceURL : https://stackoverflow.com/questions/52602990/why-cant-we-call-threadsleep-directly-inside-a-lambda-function

반응형