2014-05-11 35 views
2

在Java 8中,它看起来像一个类的lambdas保存在一个数组中。例如,让我们说我们有这个类:获取类的列表lambdas

public class LambdaFactory { 
    public Supplier<Integer> getOne(){ 
     return() -> 42; 
    } 

    public Supplier<Integer> getTwo(){ 
     return() -> 128; 
    } 

    public Supplier<Integer> getThree(){ 
     return() -> 3; 
    } 
} 

,然后我把它打印出来,像这样:

System.out.println(factory.getOne()); 
    System.out.println(factory.getOne()); 
    System.out.println(factory.getTwo()); 
    System.out.println(factory.getThree()); 

输出会像

[email protected] 
[email protected] 
[email protected] 
[email protected] 

所以我们可以看到这里有两件事。被称为两次的相同lambda给了我们相同的lambda对象(这与每次我们可以获得新内部类的anon内部类不一样)。我们也看到他们看起来像是被保存在某种类型的“Lambda”结构中,这是类的一部分

我的问题是,我可以在课堂上看到lambda表达式吗?我没有任何理由这样做,我只是喜欢解剖东西

+0

是什么让你觉得他们在一个数组? – assylias

+3

我认为你在这里得出错误的结论。匿名内部类通常将'$ 1'构造作为其名称的一部分 - 这是一个常见的习惯用法,即带有美元符号的类名是在运行时构建的,例如使用动态Proxy类时。 –

+0

我想我不知道它是一个数组还是它是什么,但lambda表达式是一样的。如果我对getOne的两个供应商执行jUnit Assert.assertSame,它将通过,但如果它是annon内部类,则会失败。请参阅https://github.com/ryber/J8Lambda/blob/master/src/test/java/examples/ObjectLifeCycle.java – ryber

回答

10

lambda是由JRE创建的,它们的创建方式是由JRE控制的,并且可能会因不同的JRE供应商而有所不同,并可能在未来发生变化版本。

如果你想有乐趣,你可以在运行时创建一个lambda它具有类文件中没有相应的信息:

import java.lang.invoke.*; 

public class ManualLambda { 
    public static void main(String[] args) throws Throwable { 
    MethodHandles.Lookup me=MethodHandles.lookup(); 
    MethodType t=MethodType.methodType(void.class); 
    MethodType rt=MethodType.methodType(Runnable.class); 
    CallSite site = LambdaMetafactory.metafactory(
     me, "run", rt, t, me.findStatic(ManualLambda.class, "sayHello", t), t); 
    MethodHandle factory=site.getTarget(); 
    Runnable r=(Runnable)factory.invoke(); 

    System.out.println("created lambda: "+r); 
    r.run(); 
    } 
    private static void sayHello() { 
     System.out.println("hello world"); 
    } 
} 

上面的代码回溯创建拉姆达时会发生什么。但是对于编译时(“真实”)lambda表达式,整个事件由单个的invokedynamic字节码指令触发。 LambdaMetafactory.metafactory(…)方法是引导程序第一次执行invokedynamic指令时调用的方法。返回的CallSite对象与invokedynamic指令永久关联。如果CallSiteConstantCallSite及其MethodHandle在每次执行时都返回相同的lambda对象,则invokedynamic指令将永远“产生”相同的lambda实例。

2

The Java Language Specification states

在运行时,一个lambda表达式的评估是类似于类实例创建表达的 评价,只要正常 完成产生对一个对象的引用。 [...]

具有以下属性的类的新实例是 已分配和初始化,或者引用了具有以下属性的 属性的现有实例。

[...]

这些规则是为了提供灵活性, Java编程语言的实现,在于:

  • 一个新的对象不一定在每一个评估来分配。
  • [...]

因此,它是由编译器或运行时环境来决定,当一个lambda表达式求什么应该被退回。

我的问题是,我可以在课堂上看到lambdas吗?我不 有任何理由这样做,我只是喜欢解剖事情

可以认为一个lambda表达式作为任何其它类常量的,一个String,一个整数文字,等等,这些都是常量出现在.class文件的常量池中。这些是对运行时创建和存在的对象的引用。无法从类的常量池中引用实际对象。

在lambda的情况下,它无论如何不会有帮助,因为它实际上可能不是同一个对象。