除了保存代码行之外,lambda表达式还有什么用处吗?

Vik*_*ash 117 java lambda java-8

除了保存代码行之外,lambda表达式还有什么用处吗?

lambda提供的特殊功能是否解决了不容易解决的问题?我见过的典型用法是代替写这个:

Comparator<Developer> byName = new Comparator<Developer>() {
  @Override
  public int compare(Developer o1, Developer o2) {
    return o1.getName().compareTo(o2.getName());
  }
};
Run Code Online (Sandbox Code Playgroud)

我们可以使用lambda表达式来缩短代码:

Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 114

Lambda表达式不会改变您通常可以解决的问题集,但肯定会使解决某些问题变得更容易,只是出于同样的原因我们不再使用汇编语言进行编程.从程序员的工作中删除冗余任务可以使生活更轻松,并且允许执行您甚至不会触摸的事情,只需要生成(手动)生成的代码量.

但lambda表达式不只是保存代码行.Lambda表达式允许您定义函数,您可以使用匿名内部类作为解决方法,这就是为什么您可以在这些情况下替换匿名内部类,但一般情况下不能.

最值得注意的是,lambda表达式是独立定义的,它们将被转换为它们的功能接口,因此没有可以访问的继承成员,而且,它们无法访问实现功能接口的类型的实例.在lambda表达式中,this并且super具有与周围上下文中相同的含义,请参阅此答案.此外,您无法创建影响周围上下文的局部变量的新局部变量.对于定义函数的预期任务,这会删除大量错误源,但它也暗示对于其他用例,可能存在匿名内部类,即使实现了功能接口,也无法将其转换为lambda表达式.

此外,该构造new Type() { … }保证产生一个新的不同实例(new一如既往).如果在非static上下文中创建,匿名内部类实例始终保持对其外部实例的引用.相反,lambda表达式仅this在需要时捕获引用,即,如果它们访问this或是非static成员.并且它们生成有意未指定标识的实例,这允许实现在运行时决定是否重用现有实例(另请参阅" 每次执行时,lambda表达式是否在堆上创建对象? ").

这些差异适用于您的示例.您的匿名内部类构造将始终生成一个新实例,它也可以捕获对外部实例的引用,而您(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName())是一个非捕获的lambda表达式,在典型实现中将评估为单个实例.此外,它不会.class在您的硬盘驱动器上生成文件.

鉴于语义和性能两者的差异,lambda表达式可能会改变程序员将来解决某些问题的方式,当然,也是由于新的API包含了利用新语言功能的函数式编程思想.另请参见Java 8 lambda表达式和第一类值.

  • @EricDuminil:对我而言,图灵完整性太过理论化.例如,无论Java是否完成图灵,您都无法用Java编写Windows设备驱动程序.(或者在PostScript中,也是图灵完成的)向Java添加允许这样做的功能,将改变您可以用它解决的问题集,但是,这不会发生.所以我更喜欢集会点; 因为您可以做的一切都是在可执行的CPU指令中实现的,所以在Assembly中没有什么不能做的,它支持每个CPU指令(比图灵机的形式更容易理解). (3认同)
  • @abelito对所有高级编程结构都是很普遍的,以减少对“实际发生的事情的可见性”,因为这就是全部,更少的细节,而是更多地关注实际的编程问题。您可以使用它来使代码更好或更糟。这只是一个工具。像投票功能一样;它是一种可以按预期方式使用的工具,可以对帖子的实际质量提供有根据的判断。或者,您只是因为讨厌lambda,而用它来拒绝详尽而合理的答案。 (2认同)

Jar*_*ith 49

编程语言不适用于机器执行.

它们是供程序员思考的.

语言是与编译器的对话,可以将我们的想法变成机器可以执行的东西.一位来自谁走到它与其他语言(或人关于Java的主诉离开其他语言)曾经是,它迫使对程序员有一定的心理模型(即一切都是一个类).

我不打算判断这是好还是坏:一切都是权衡取舍.但Java 8 lambdas允许程序员根据函数进行思考,这是您以前在Java中无法做到的.

这是同样的事情作为一个程序的程序员学习思考中的类而言,当他们来到Java的:你看他们是荣耀的结构类逐渐上移,并有一堆的静态方法"助手"类和移动上的东西,更接近于理性的OO设计(mea culpa).

如果您只是将它们视为表达匿名内部类的较短方式,那么您可能不会发现它们非常令人印象深刻,就像上面的程序程序员可能认为类没有任何重大改进一样.

  • 但要小心,我曾经认为OOP就是一切,然后我对实体组件系统架构感到非常困惑(whaddaya意味着你没有针对每种游戏对象的类?!和每个游戏对象是不是OOP对象?!) (4认同)
  • 换句话说,在**OOP中思考**,而不仅仅是将**o*f*类视为另一种工具,y以一种糟糕的方式限制了我的思维. (3认同)
  • @immibis:“以OOP进行思考”有很多不同的方法,因此,除了将“在OOP中进行思考”与“在类中进行思考”相混淆的常见错误外,还有许多以适得其反的方式进行思考的方法。不幸的是,这种情况从一开始就经常发生,因为即使是书籍和教程也要教那些次等的东西,在示例中显示反例并传播这种思想。所谓的“对每种事物都有一个类”的假设仅仅是一个例子,与抽象的概念相矛盾,同样,似乎存在着“ OOP意味着编写尽可能多的样板”的神话,等等。 (2认同)

Era*_*ran 35

如果能够以更短更清晰的方式编写大量逻辑,则可以将保存代码行视为一项新功能,从而缩短其他人阅读和理解的时间.

没有lambda表达式(和/或方法引用),Stream管道的可读性就会低得多.

例如,Stream如果用一个匿名类实例替换每个lambda表达式,请考虑以下管道的外观.

List<String> names =
    people.stream()
          .filter(p -> p.getAge() > 21)
          .map(p -> p.getName())
          .sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
          .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

这将是:

List<String> names =
    people.stream()
          .filter(new Predicate<Person>() {
              @Override
              public boolean test(Person p) {
                  return p.getAge() > 21;
              }
          })
          .map(new Function<Person,String>() {
              @Override
              public String apply(Person p) {
                  return p.getName();
              }
          })
          .sorted(new Comparator<String>() {
              @Override
              public int compare(String n1, String n2) {
                  return n1.compareToIgnoreCase(n2);
              }
          })
          .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

这比使用lambda表达式的版本更难写,而且更容易出错.这也很难理解.

而这是一条相对较短的管道.

为了在没有lambda表达式和方法引用的情况下使其可读,您必须定义包含此处使用的各种功能接口实例的变量,这将拆分管道的逻辑,使其更难理解.

  • 我认为这是一个关键的洞察力.可以说,如果语法和认知开销太大而无法使用,那么在编程语言中几乎不可能做到某些事情,因此其他方法将更加优秀.因此,通过减少语法和认知开销(如lambdas与匿名类相比),上述管道变得可能.如果写一段代码让你从工作中解雇,可以说这是不可能的. (17认同)

als*_*her 8

内部迭代

在迭代Java Collections时,大多数开发人员倾向于获取一个元素然后进行处理.这是,取出该项目,然后使用它,或重新插入它等.使用8版之前的Java,您可以实现内部类,并执行以下操作:

numbers.forEach(new Consumer<Integer>() {
    public void accept(Integer value) {
        System.out.println(value);
    }
});
Run Code Online (Sandbox Code Playgroud)

现在使用Java 8,您可以做得更好,更简洁:

numbers.forEach((Integer value) -> System.out.println(value));
Run Code Online (Sandbox Code Playgroud)

或更好

numbers.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

行为作为参数

猜猜以下情况:

public int sumAllEven(List<Integer> numbers) {
    int total = 0;

    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    } 
    return total;
}
Run Code Online (Sandbox Code Playgroud)

使用Java 8 Predicate接口,您可以做得更好:

public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;

    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}
Run Code Online (Sandbox Code Playgroud)

把它称为:

sumAll(numbers, n -> n % 2 == 0);
Run Code Online (Sandbox Code Playgroud)

来源:DZone - 为什么我们需要Java中的Lambda表达式

  • 我没有问lambda表达式是什么,我只是说内部迭代与lambda表达式无关. (5认同)
  • 内部迭代如何成为 lambda 功能?简单的事实是,从技术上讲,lambdas 不允许您执行在 java-8 之前无法执行的任何操作。这只是一种_更简洁_的传递代码的方式。 (4认同)

hol*_*ava 5

使用 lambda 代替内部类有很多好处,如下所示:

  • 使代码更加紧凑和富有表现力,而无需引入更多的语言语法语义。你已经在你的问题中举了一个例子。

  • 通过使用 lambda,您很乐意在元素流上使用函数式操作进行编程,例如集合上的映射缩减转换。请参阅java.util.functionjava.util.stream包文档。

  • 编译器没有为 lambda 生成物理类文件。因此,它使您交付的应用程序更小。内存如何分配给lambda?

  • 如果 lambda 不访问超出其范围的变量,编译器将优化 lambda 创建,这意味着 lambda 实例仅由 JVM 创建一次。有关更多详细信息,您可以查看@Holger 对问题Is method reference caching a good idea in Java 8? 的回答。

  • 除了函数式接口之外,Lambda 还可以实现多标记接口,但匿名内部类不能实现更多接口,例如:

    //                 v--- create the lambda locally.
    Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
    
    Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

17912 次

最近记录:

8 年,2 月 前