java multithreading lambda java-8

java - ¿Por qué no podemos llamar Thread#sleep() directamente dentro de una función Lambda?



multithreading java-8 (8)

El siguiente código funciona:

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

Esto se debe a que sleep(int milliseconds) es un método de la clase Thread mientras crea y pasa una instancia Runnable al constructor de la clase Thread .

En el segundo método, está creando una instancia de clase interna anónima de clase Thread y, por lo tanto, tiene acceso a todos los métodos de clase Thread .

El siguiente código me está dando un error de tiempo de compilación:

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

El método sleep (int) no está definido para el tipo A (donde A es mi nombre de clase).

Considerando que, cuando uso una clase interna anónima, no hay error de tiempo de compilación:

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

El siguiente código también funciona bien:

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

¿Cómo funcionan las cosas dentro de un cuerpo de expresión lambda? Por favor ayuda.

De muchas respuestas, puedo ver que el error se puede resolver utilizando Thread.sleep(1000) en mi primer enfoque. Sin embargo, realmente apreciaría si alguien pudiera explicarme cómo funcionan el alcance y el contexto en una expresión lambda.


En el primer enfoque, está pasando un Runnable al Thread , necesita llamar a Thread.sleep :

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

Es la versión corta de:

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

Mientras que en el segundo, está anulando el método thread.run directamente, por lo que está bien llamar a thread.sleep en thread.run .


Esto termina siendo un malentendido de Scope.

Cuando pasa un lambda al subproceso, no está creando una subclase de subproceso, en su lugar, está pasando el FunctionalInterface de Runnable y llamando a un constructor de subproceso. Cuando intenta llamar a Sleep, el contexto de su alcance es una combinación de Runnable + su clase (puede llamar a los métodos predeterminados si la interfaz Runnable los tenía), no Thread.

Runnable no tiene sleep () definido, pero Thread sí.

Cuando creas una clase interna anónima, estás subclasificando Thread, por lo que sleep () está disponible para que llames, ya que el contexto de Scope es una subclase de Thread.

No se recomienda el uso de métodos estáticos sin el nombre de la clase por este tipo de malentendidos. Usar Thread.Sleep es correcto e inequívoco en todas las circunstancias.


Me gusta la respuesta que se proporcionó y aceptó, pero en palabras mucho más simples, puede pensar que this ha cambiado de una clase interna anónima a una lambda.

En el caso de un AIC, this refiere a una instancia de una clase que está extendiendo (en su ejemplo, al ser Thread ), en el caso de la lambda expression , se refiere a una instancia de la clase que rodea a la expresión lambda (cualquiera que sea la clase) en su ejemplo). Y apuesto a que en tu clase usas la expresión lambda, no hay tal sleep definido.


Su duda se origina en un malentendido sobre cómo se definen los ámbitos de una expresión lambda y una clase anónima. A continuación, trataré de aclarar esto.

Las expresiones Lambda NO introducen un nuevo nivel de alcance. Esto significa que, dentro de él, solo puede acceder a las mismas cosas a las que podría tener acceso en el bloque de código de cierre inmediato. Vea lo que dicen los documentation :

Las expresiones Lambda tienen un alcance léxico. Esto significa que no heredan ningún nombre de un supertipo ni introducen un nuevo nivel de alcance. Las declaraciones en una expresión lambda se interpretan de la misma manera que en el entorno envolvente.

Las clases anónimas funcionan de manera diferente. Ellos introducen un nuevo nivel de alcance. Se comportan como una clase local (una clase que declara dentro de un bloque de código), aunque no pueden tener constructores. Vea lo que dicen los docs :

Al igual que las clases locales, las clases anónimas pueden capturar variables; Tienen el mismo acceso a las variables locales del ámbito de aplicación:

  • Una clase anónima tiene acceso a los miembros de su clase adjunta.
  • Una clase anónima no puede acceder a las variables locales en su ámbito que no se declaran como final o efectivamente final.
  • Al igual que una clase anidada, una declaración de un tipo (como una variable) en una clase anónima oculta cualquier otra declaración en el ámbito que tiene el mismo nombre. Ver Shadowing para más información.

En este contexto, la clase anónima actuará como una clase local dentro de Thread y, por lo tanto, podrá acceder directamente a sleep() , ya que este método estará dentro de su alcance. Sin embargo, en la expresión lambda, sleep() no estará dentro de su alcance (no puede llamar a sleep() en el entorno Thread.sleep() ), por lo que debe usar Thread.sleep() . Tenga en cuenta que este método es estático y, por lo tanto, no requiere una instancia de su clase para poder ser llamado.


Thread.sleep es un método estático ...

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


Thread.sleep es un método estático en la clase Thread .

La razón por la que puede llamar a sleep directamente sin ningún calificador en una clase anónima es porque realmente está en el contexto de una clase que hereda de Thread . Por lo tanto, el sleep es accesible allí.

Pero en el caso lambda, no estás en una clase que hereda de Thread . Estás dentro de cualquier clase alrededor de ese código. Por lo tanto, no se puede llamar directamente a Thread.sleep y debe decir Thread.sleep . La documentation también apoya esto:

Las expresiones Lambda tienen un alcance léxico. Esto significa que no heredan ningún nombre de un supertipo ni introducen un nuevo nivel de alcance. Las declaraciones en una expresión lambda se interpretan de la misma manera que en el entorno envolvente.

Básicamente, eso significa que dentro de la lambda, en realidad estás en el mismo ámbito que si estuvieras fuera de la lambda. Si no puede acceder a sleep fuera de la lambda, tampoco puede hacerlo en el interior.

Además, tenga en cuenta que las dos formas de crear un hilo que ha mostrado aquí son intrínsecamente diferentes. En el lambda, estás pasando un Runnable al constructor de Thread , mientras que en el de la clase anónima, estás creando un Thread al crear una clase anónima directamente.


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

es equivalente a

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

así que el compilador no buscará el sleep en Thread