Java 中分配给功能接口的对象是什么?

What is the object assigned to a Functional Interface in Java?

我是 Java 中的 Lambda 表达式的新手,所以如果这个问题看起来很愚蠢,请不要反应太过苛刻: 实现的实际对象是什么我把它分配给一个FunctionalInterface?即,如果我执行 List<String> lst = new ArrayList<>(),我将实现一个 ArrayList 对象。如果我这样做 Function<String,String> fct = s -> s,创建的对象是什么?该对象是不可变的吗,即 public static final Function<String,String> REUSE_OFTEN = s -> String.valueOf(s)?

有意义吗?

当您编写 lambda 表达式时 -

Function<String,String> fct = s -> s;

它只是创建了一个匿名的 class,实现了 Function<String, String> 接口,并用 s -> s 提供的实现覆盖了它唯一的方法 apply()。并将此对象分配给 Function<String, String> 引用。

Function<String,String> fct = new Function<>() {
    @Override
    public String apply(String s) {
        return s;
    }
};

它是一个不可变的对象,拥有一个 public static final Function<String,String> REUSE_OFTEN = s -> String.valueOf(s) 确实有意义,因为您可以重复使用相同的对象来对不同的输入应用此操作。

I.e., if I do List lst = new ArrayList<>() I implement an ArrayList object.

你实例化一个 ArrayList 而不是。

What is the object that gets created if I do Function fct = s -> s? And is that object immutable, i.e. does it make sense to have a public static final Function REUSE_OFTEN = s -> String.valueOf(s)?

Lambda 在字节码方面与匿名 classes 不同,在 behavior/functioning 方面也不相同。最后一个足够接近但不完全相同。

这里有两种方法做同样的事情,一种使用 lambda,另一种使用匿名 class :

public class CompareBoth {

    public void lambda() {
        Function<String,String> fct = s -> s;
        fct.apply("a");
    }


    public void anonymousClass() {
        Function<String,String> fct = new Function<String, String>() {

            @Override
            public String apply(String t) {
                return t;
            }

        };
        fct.apply("a");
    }
}

这里是反汇编代码:

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

  public void lambda();
    Code:
       0: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
       5: astore_1
       6: aload_1
       7: ldc           #3                  // String a
       9: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      14: pop
      15: return

  public void anonymousClass();
    Code:
       0: new           #5                  // class cli/CompareBoth
       3: dup
       4: aload_0
       5: invokespecial #6                  // Method cli/CompareBoth."<init>":(Lcli/CompareBoth;)V
       8: astore_1
       9: aload_1
      10: ldc           #3                  // String a
      12: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      17: pop
      18: return
}

我们可以看到 JVM 对它们的处理方式不同。

What is the actual object that gets implemented ?

创建 lambda 时会执行此 JVM 指令:

0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;

Java7中引入的

InvokeDynamic被指定为字节码指令,通过动态方法调用促进动态语言(对于JVM)的实现。 您可以阅读 this javaworld page 以获得更多信息。
现在,随着你的发展,这真的很重要吗?
在大多数情况下,它不会。
功能接口在功能上与匿名接口非常接近,但有许多细微差别。
从概念上讲,我认为这样考虑是可以接受的。

关于函数式接口不变性:

函数式接口可能有一个状态,但被设计为不可变的。

因此,如果有意义,您可以重复使用它们。
但是InvokeDynamic可能会被JVM优化。
所以你不应该为了节省一些对象而在你的代码中重用它们。
而不是将功能接口提取到常量或变量中,因为它使事情更清楚或避免重复。