DEV Community

Discussion on: [PT-BR] Lambda Expressions não são classes anônimas?

Collapse
 
wldomiciano profile image
Wellington Domiciano

Nossa, Max, acho que eu nunca tinha me feito esta pergunta e seu post foi uma excelente provocação e ponto de partida!

Pesquisando além, olha o que eu descobri:

Como vc disse, expressões lambda não são classes anônimas e o próprio Java nos informa isto.

import java.util.function.Consumer;

public class App {
  public static void main(String... args) {
    Consumer<Integer> consumer1 = e -> {
    };

    Consumer<Integer> consumer2 = new Consumer<Integer>() {
      @Override
      public void accept(Integer t) {
      }
    };

    System.out.println(consumer1.getClass().isAnonymousClass()); // false
    System.out.println(consumer2.getClass().isAnonymousClass()); // true
  }
}
Enter fullscreen mode Exit fullscreen mode

O compilador realmente transforma a lambda em métodos. O código abaixo...

import java.util.stream.Stream;

public class App {
  public static void main(String... args) {
    Stream.of(args)
      .map(e -> Integer.valueOf(e))
      .forEach(e -> System.out.println(e));
  }
}
Enter fullscreen mode Exit fullscreen mode

... Vira isso depois de compilado quando vc roda javap -c -p App:

Compiled from "App.java"
public class App {
  public App();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String...);
    Code:
       0: aload_0
       1: invokestatic  #7                  // InterfaceMethod java/util/stream/Stream.of:([Ljava/lang/Object;)Ljava/util/stream/Stream;
       4: invokedynamic #13,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
       9: invokeinterface #17,  2           // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
      14: invokedynamic #21,  0             // InvokeDynamic #1:accept:()Ljava/util/function/Consumer;
      19: invokeinterface #25,  2           // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
      24: return

  private static void lambda$main$1(java.lang.Integer);
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: invokevirtual #35                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
       7: return

  private static java.lang.Integer lambda$main$0(java.lang.String);
    Code:
       0: aload_0
       1: invokestatic  #41                 // Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer;
       4: areturn
}
Enter fullscreen mode Exit fullscreen mode

Só que não pára por aí. No runtime, segundo a especificação, o valor de uma lambda é uma referência a uma instância de uma classe que implementa a interface funcional desejada. Veja:

docs.oracle.com/javase/specs/jls/s...

The value of a lambda expression is a reference to an instance of a class with the following properties:

  • The class implements the targeted functional interface type and, if the target type is an intersection type, every other interface type mentioned in the intersection.
  • Where the lambda expression has type U, for each non-static member method m of U:
  • If the function type of U has a subsignature of the signature of m, then the class declares a method that overrides m. The method's body has the effect of evaluating the lambda > body, if it is an expression, or of executing the lambda body, if it is a block; if a result is expected, it is returned from the method.
  • If the erasure of the type of a method being overridden differs in its signature from the erasure of the function type of U, then before evaluating or executing the lambda body, > the method's body checks that each argument value is an instance of a subclass or subinterface of the erasure of the corresponding parameter type in the function type of U; if > not, a ClassCastException is thrown.
  • The class overrides no other methods of the targeted functional interface type or other interface types mentioned above, although it may override methods of the Object class.

Isso dá pra gente atestar assim:

import java.util.function.Consumer;

public class App {
  public static void main(String... args) {
    Consumer<Integer> consumer = e -> {
    };

    System.out.println(consumer instanceof Consumer); // true
  }
}
Enter fullscreen mode Exit fullscreen mode

Só que a parte mais legal é que eu descobri através desta resposta que é possível ver como a classe gerada é no runtime usando o seguinte commando:

java -Djdk.internal.lambda.dumpProxyClasses=. App
Enter fullscreen mode Exit fullscreen mode

O código acima geraria estes arquivos:

.
├── App$$Lambda$1.class
├── App.class
├── App.java
Enter fullscreen mode Exit fullscreen mode

E rodando javap -c -p App\$\$Lambda\$1 dá pra gente ver:

final class App$$Lambda$1 implements java.util.function.Consumer {
  private App$$Lambda$1();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public void accept(java.lang.Object);
    Code:
       0: aload_1
       1: checkcast     #14                 // class java/lang/Integer
       4: invokestatic  #20                 // Method App.lambda$main$0:(Ljava/lang/Integer;)V
       7: return
}
Enter fullscreen mode Exit fullscreen mode

Isso tudo é muito interessante! Eu testei usando Java 17.

Collapse
 
dearrudam profile image
Maximillian Arruda

Que massa!!! Só essa sua resposta daria um ótimo artigo!!! Obrigado por compartilhar!!! Abraços!!!!

Collapse
 
j_a_o_v_c_t_r profile image
João Victor Martins

Wow, excelente. Concordo com o Max. Isso deveria ser um post haha!! E ai, anima?

Collapse
 
wldomiciano profile image
Wellington Domiciano

Valeu! Seria legal escrever sobre, eu gostaria de tentar, mas eu sou enrolado para escrever.

Bem que o Max podia fazer uma parte 2 ou mesmo incrementer este post mostrando a parada do -Djdk.internal.lambda.dumpProxyClasses porque eu achei bem massa poder ver estes detalhes!