Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么lambda翻译需要生成静态方法?_Java_Lambda_Jvm_Java 8_Bytecode - Fatal编程技术网

Java 为什么lambda翻译需要生成静态方法?

Java 为什么lambda翻译需要生成静态方法?,java,lambda,jvm,java-8,bytecode,Java,Lambda,Jvm,Java 8,Bytecode,Lambda转换是一个两步过程,One:将Lambda分解为同一类中的静态方法 public class Main { public static void main(String[] args) { Runnable r = () -> System.out.println("Hello"); System.out.println(Arrays.asList(Main.class.getDeclaredMethods())); } } [

Lambda转换是一个两步过程,One:将Lambda分解为同一类中的静态方法

public class Main {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("Hello");
        System.out.println(Arrays.asList(Main.class.getDeclaredMethods()));
    }
}
[private static void Main.lambda$Main$0(),public static void Main.Main(java.lang.String[])]

Two:生成实现函数接口的类

System.out.println("A class has been generated: " + r.getClass());
System.out.println("That implements a Functional Interface: " + Arrays.asList(r.getClass().getInterfaces()));
已生成一个类:类主$$Lambda$1/149928006

它实现了一个功能接口:[接口java.lang.Runnable]

问题这个静态方法需要什么?为什么不能将lambda主体直接放入接口方法中?比如:

class Main$$Lambda$1 {
    public void run() {
        /* Lambda body here */
    }
}

因为这样实际上更便宜。在第一次调用期间动态地从方法生成lambda比通过类加载器加载单独的类要好。在内部,它使用了
不安全的.defineAnonymousClass
,这是一个比普通类更轻的类。这样的“lambda类”不绑定到任何类装入器,因此在不再需要时可以轻松地进行垃圾收集。另外,我猜有计划使这种机制更轻、更快。对于普通的匿名类,这是不可能的,因为从JVM的角度来看,这些类与普通类没有区别,而且更重。

您的测试是不完整的

public class Lambda {

  private String hello = "Hello from instance";

  public static void main(String[] args) {
    Runnable r = () -> System.out.println("Hello");
    for (Method m: Lambda.class.getDeclaredMethods()) {
      System.out.println(m);
    }
  }

  public void instanceMethodWithAccessToInstanceVariables(){
    Runnable r = () -> System.out.println(hello);
  }

  public void instanceMethodWithoutAccessToInstanceVariables(){
    Runnable r = () -> System.out.println("Hello from instance");
  }
}
其结果如下:

public void Lambda.instanceMethodWithAccessToInstanceVariables()
public void Lambda.instanceMethodWithoutAccessToInstanceVariables()
private static void Lambda.lambda$instanceMethodWithoutAccessToInstanceVariables$2()
private void Lambda.lambda$instanceMethodWithAccessToInstanceVariables$1()
private static void Lambda.lambda$main$0()
public static void Lambda.main(java.lang.String[])
这清楚地表明了几个情况:

  • 静态方法中的lambda声明静态方法
  • lambda在实例方法中使用实例变量声明实例方法
  • 不使用实例变量的实例方法中的lambda声明静态方法
前两个是相当合乎逻辑的。为什么希望静态成员访问实例成员?实例方法也是如此

真正的问题是,为什么不使用任何实例变量的实例方法声明为静态方法


好的,这也是出于Tagir提到的性能和安全原因。

除了这里给出的正确答案(因为当前的方案更有效,减少了lambda的捕获/链接成本并减少了代码重复),还有一些其他原因说明您的想法根本没有意义

  • 字节码从何而来?lambda代理类是在运行时生成的,而不是在编译时生成的。如果我们要将字节码塞进代理类中,它必须来自某个地方。这意味着我们必须将其放入捕获类文件中,然后将其复制到代理类中。在这里,它只是生活在捕捉类,我们做了
  • 访问控制。如果lambda主体调用私有方法呢?通过将其分解到捕获类中,它自动获取捕获类的访问控制上下文(它在逻辑上是该类的一部分)。如果我们将字节码放入代理类中,我们必须执行额外的魔法来为其提供正确的访问控制上下文

请您再详细说明一下好吗?我不能比加载一个单独的类更好地理解
。在任何情况下都需要生成一个类来实现函数接口。你说的是什么独立类?
对于普通的匿名类
我不主张使用匿名内部类实现lambda。让它们使用轻量级魔术类来实现。但是直接将lambda代码添加到它们中。我假设这些特殊类可以在内部保存代码。假设我们应该按照您的建议创建
类Main$$Lambda$1
。然后我们应该在
Main
类旁边创建单独的.class文件(可能在同一个JAR中)。所以这个类文件就是我所说的“独立类”。这是一个在应用程序启动之前就存在的普通类,JVM应该将其作为普通类加载。但是lambda类不存在于硬盘上,并且在运行时动态生成。因此,问题是在javac编译之后但在应用程序启动之前,将lambda函数体的代码放在何处。当前的解决方案是将其作为单独的方法存储在定义lambda的类中。你建议哪种选择?这是有道理的
LambdaMetafactory.metafactory()
需要为提供lambda代码的方法提供
MethodHandle
。这个句柄必须是一个独立的方法,不能链接到方法的中间。我做对了吗?也许我没有理解对,但这并不能解决我的问题。为什么要生成任何方法?为什么不将lambda主体移动到生成的类中。这只是为了保持变量和访问的范围相同。具有讽刺意味的是,生成的代理类需要这种“额外的魔力”,即使lambda表达式不使用
private
特性,因为保存lambda主体的生成方法本身就是
private
。但是,如果接口实例本身不在范围内,则更容易保留范围规则。想想
super
调用等。