有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java Lambdas和闭包

我听说Lambda很快就要来到你附近的Java(J8)。我在某个博客上找到了一个它们的样子示例:

SoccerService soccerService = (teamA, teamB) -> {
    SoccerResult result = null;
    if (teamA == teamB) {
        result = SoccerResult.DRAW;
    }
    else if(teamA < teamB) {
        result = SoccerResult.LOST;
    }
    else {
        result = SoccerResult.WON;
    }

    return result;
};

所以马上:

  • teamAteamB在哪里键入?或者它们不是(像某种奇怪的泛型)
  • lambda是闭包的类型,还是相反
  • 与典型的匿名函数相比,这会给我带来什么好处

共 (3) 个答案

  1. # 1 楼答案

    Lambda表达式只是实现目标接口的语法糖,这意味着您将通过Lambda表达式实现接口中的特定方法。编译器可以推断接口中参数的类型,这就是为什么不需要在lambda表达式中显式定义它们

    例如:

    Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);
    

    在这个表达式中,lambda表达式显然实现了Comparator个字符串,因此,这意味着lambda表达式是实现compare(String, String)的语法糖

    因此,编译器可以安全地假定s1s2的类型为String

    目标接口类型提供编译器确定lambda参数的实际类型所需的所有信息

    OracleCorparty的Java语言架构师BriantGoetz发表了两篇关于JDK8Lambdas中正在进行的工作的文章。我相信你的问题的答案是:

    第二篇文章解释了lambda表达式是如何在字节码级别实现的,并可能帮助您深入了解第二个问题的细节

  2. # 2 楼答案

    有关该示例的完整版本,请参见this page(不过,相关部分如下所示)

    这些类型是从SoccerService接口和SoccerResult枚举推断出来的,不会显示在代码段中:

    enum SoccerResult{
        WON, LOST, DRAW
    }
    
    interface SoccerService {
        SoccerResult getSoccerResult(Integer teamA, Integer teamB);
    }
    

    lambdas与标准匿名类相比的好处在于减少了冗长性:

    (x, y) => x + y
    

    而不是像:

    new Adder()
    {
      public int add(int x, int y)
      {
        return x + y;
      }
    }
    

    有关闭包和lambda之间的区别,请参见this question

  3. # 3 楼答案

    • teamA和teamB在哪里输入?或者它们不是(像某种奇怪的泛型)

    Lambda使用目标类型,很像泛型方法调用(自1.5起)和菱形运算符(自1.7起)。粗略地说,其中说明(或可以推断)应用结果的类型,该类型用于提供单一抽象方法(SAM)基类型的类型,从而提供方法参数类型

    作为1.5中通用方法推断的示例:

    Set<Team> noTeams = Collections.emptySet(); 
    

    和1.7中的菱形运算符:

    Set<Team> aTeams = new HashSet<>();
    

    团队,团队,团队,团队,团队,团队,团队。我甚至喜欢说团队这个词

    • lambda是闭包的一种类型,还是相反

    lambda是一种有限形式的闭包,其方式与匿名内部类几乎完全相同,但存在一些随机差异:

    外部this不被内部this隐藏。这意味着lambda和匿名内部类中的相同文本可能意味着微妙但完全不同的事物。这将使堆栈溢出忙于处理奇怪的问题

    为了弥补内部this的不足,如果直接指定给局部变量,则该值可以在lambda中访问。IIRC(我可以检查,但不会),在一个匿名的内部类中,局部变量将位于作用域中,并将变量隐藏在外部作用域中,但您不能使用它。我相信由于缺少实例初始化器,因此更容易指定

    没有标记final但可以标记的本地字段被视为final。所以它们不在范围内,但实际上您可以读取(尽管不写入)它们

    • 与典型的匿名函数相比,这会给我带来什么好处

    更简洁的语法。就这样

    当然,Java语法的其余部分和以往一样糟糕得令人绝望

    我不相信这是在最初的实现中,但是lambdas可以使用方法句柄,而不是作为[internal]类实现。方法句柄的性能在某种程度上低于早期的预测。去掉类,应该减少字节码占用,可能会减少运行时占用,当然还有类加载时间。可能存在这样一种实现:大多数匿名内部类(不是Serializable,普通的静态初始化器)没有经过构思糟糕的类加载机制,没有任何特别明显的不兼容性

    (希望我的术语wrthiding是正确的。)