java - while - Warum können wir Thread#sleep() nicht direkt in einer Lambda-Funktion aufrufen?




thread sleep java api (6)

Der folgende Code gibt mir einen Fehler bei der Kompilierung:

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

Die Methode sleep (int) ist für den Typ A undefiniert (wobei A mein Klassenname ist).

Wenn ich hingegen eine anonyme innere Klasse verwende, tritt kein Fehler bei der Kompilierung auf:

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

Der folgende Code funktioniert auch gut:

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

Wie funktionieren die Dinge in einem Lambda-Ausdruckskörper? Bitte helfen Sie.

Aus vielen Antworten kann ich Thread.sleep(1000) dass der Fehler mit Thread.sleep(1000) in meinem ersten Ansatz behoben werden Thread.sleep(1000) . Ich wäre jedoch sehr dankbar, wenn mir jemand erklären könnte, wie Umfang und Kontext in einem Lambda-Ausdruck funktionieren.


Dies führt zu einem Missverständnis von Scope.

Wenn Sie ein Lambda an den Thread übergeben, erstellen Sie keine Unterklasse von Thread, sondern übergeben die FunctionalInterface von Runnable und rufen einen Konstruktor von Thread auf. Wenn Sie versuchen, Sleep aufzurufen, ist der Kontext Ihres Gültigkeitsbereichs eine Kombination aus Runnable + Ihrer Klasse (Sie können Standardmethoden aufrufen, wenn die Runnable-Schnittstelle über diese verfügt), nicht Thread.

Für Runnable ist sleep () nicht definiert, für Thread jedoch.

Wenn Sie eine anonyme innere Klasse erstellen, unterteilen Sie Thread in Unterklassen, sodass Sie sleep () aufrufen können, da der Kontext des Bereichs eine Unterklasse von Thread ist.

Das Aufrufen von statischen Methoden ohne Klassennamen wird für genau diese Art von Missverständnissen nicht empfohlen. Die Verwendung von Thread.Sleep ist unter allen Umständen korrekt und eindeutig.


Folgender Code funktioniert:

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

Dies liegt daran, dass sleep(int milliseconds) eine Methode aus der Thread Klasse ist, während Sie eine Runnable Instanz erstellen und an den Thread Klassenkonstruktor übergeben.

Bei der zweiten Methode erstellen Sie eine anonyme innere Klasseninstanz der Thread Klasse und haben somit Zugriff auf alle Thread Klassenmethoden.


Ihr Zweifel beruht auf einem Missverständnis darüber, wie die Bereiche eines Lambda-Ausdrucks und einer anonymen Klasse definiert sind. Im Folgenden werde ich versuchen, dies zu klären.

Lambda-Ausdrücke führen KEINE neue Ebene des Geltungsbereichs ein. Dies bedeutet, dass Sie darin nur auf die gleichen Dinge zugreifen können, auf die Sie im unmittelbar umschließenden Codeblock zugreifen können. Sehen Sie, was die documentation sagen:

Lambda-Ausdrücke haben einen lexikalischen Gültigkeitsbereich. Dies bedeutet, dass sie keine Namen von einem Supertyp erben oder eine neue Ebene des Geltungsbereichs einführen. Deklarationen in einem Lambda-Ausdruck werden genauso interpretiert wie in der umgebenden Umgebung.

Anonyme Klassen funktionieren anders. Sie führen eine neue Ebene des Scoping ein. Sie verhalten sich ähnlich wie eine lokale Klasse (eine Klasse, die Sie in einem Codeblock deklarieren), obwohl sie keine Konstruktoren haben können. Sehen Sie, was die docs sagen:

Anonyme Klassen können wie lokale Klassen Variablen erfassen. Sie haben den gleichen Zugriff auf lokale Variablen des einschließenden Gültigkeitsbereichs:

  • Eine anonyme Klasse hat Zugriff auf die Mitglieder ihrer einschließenden Klasse.
  • Eine anonyme Klasse kann nicht auf lokale Variablen in ihrem einschließenden Bereich zugreifen, die nicht als endgültig oder effektiv endgültig deklariert sind.
  • Wie bei einer verschachtelten Klasse werden bei einer Deklaration eines Typs (z. B. einer Variablen) in einer anonymen Klasse alle anderen Deklarationen im einschließenden Bereich abgeschattet, die denselben Namen haben. Weitere Informationen finden Sie unter Shadowing.

In diesem Kontext verhält sich die anonyme Klasse wie eine lokale Klasse in Thread und kann daher direkt auf sleep() zugreifen, da diese Methode in ihrem Gültigkeitsbereich liegt. Im Lambda-Ausdruck liegt sleep() jedoch nicht in seinem Gültigkeitsbereich (Sie können sleep() in der umschließenden Umgebung nicht aufrufen), sodass Sie Thread.sleep() . Beachten Sie, dass diese Methode statisch ist und daher keine Instanz ihrer Klasse benötigt, um aufgerufen zu werden.


Im ersten Ansatz übergeben Sie ein Runnable an den Thread . Sie müssen Thread.sleep :

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

es ist die Kurzversion von:

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

Thread t2 = new Thread(runnable);

Während im zweiten thread.run Methode direkt überschrieben wird, ist es in thread.run thread.sleep in thread.run .


Thread.sleep ist eine statische Methode in der Thread Klasse.

Der Grund, warum Sie sleep direkt ohne Qualifizierer in einer anonymen Klasse aufrufen können, liegt darin, dass Sie sich tatsächlich im Kontext einer Klasse befinden, die von Thread erbt. Daher ist dort der sleep zugänglich.

Im Lambda-Fall gehören Sie jedoch nicht zu einer Klasse, die von Thread erbt. Sie befinden sich in der Klasse, die diesen Code umgibt. Daher kann der sleep nicht direkt aufgerufen werden und Sie müssen Thread.sleep sagen. Die documentation unterstützt dies auch:

Lambda-Ausdrücke haben einen lexikalischen Gültigkeitsbereich. Dies bedeutet, dass sie keine Namen von einem Supertyp erben oder eine neue Ebene des Geltungsbereichs einführen. Deklarationen in einem Lambda-Ausdruck werden genauso interpretiert wie in der umgebenden Umgebung.

Grundsätzlich heißt das, dass Sie sich innerhalb des Lambdas im selben Bereich befinden, als wären Sie außerhalb des Lambdas. Wenn Sie außerhalb des Lambda nicht sleep können, können Sie auch nicht im Inneren sleep .

Beachten Sie außerdem, dass die beiden hier gezeigten Möglichkeiten zum Erstellen eines Threads grundsätzlich unterschiedlich sind. In der Lambda-Klasse übergeben Sie ein Runnable an den Thread Konstruktor, während Sie in der anonymen Klasse einen Thread erstellen, indem Sie eine anonyme Klasse davon direkt erstellen.


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

ist äquivalent zu

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

Der Compiler sucht also nicht nach sleep im Thread







java-8