首页 > 学院 > 开发设计 > 正文

Java 终于有 Lambda 表达式啦~Java 8 语言变化——Lambda 表达式和接口类更改【转载】

2019-11-15 00:50:24
字体:
来源:转载
供稿:网友
java 终于有 Lambda 表达式啦~Java 8 语言变化——Lambda 表达式和接口类更改【转载】

原文地址 en cn

下载 Demo

Java? 8 包含一些重要的新的语言功能,为您提供了构建程序的更简单方式。Lambda 表达式 为内联代码块定义一种新语法,其灵活性与匿名内部类一样,但样板文件要少得多。接口更改使得接口可以添加到现有接口中,同时又不会破坏与现有代码的兼容性。本文将了解这些更改是如何协同工作的。

Java 8 的最大变化在于添加了对 lambda 表达式 的支持。Lambda 表达式是可按引用传递的代码块。类似于一些其他编程语言中的闭包:它们是实现某项功能的代码,可接受一个或多个输入参数,而且可返回一个结果值。闭包是在一个上下文中定义的,可访问(对于 lambda 表达式而言是只读访问)来自上下文的值。

如果您不熟悉闭包,不用害怕。Java 8 lambda 表达式其实是匿名内部类的一种特殊化,而几乎所有 Java 开发人员都熟悉匿名内部类。匿名内部类提供了一个接口的内联实现,或者一个基类的子类,我们一般只会在代码中的一个地方使用它。Lambda 表达式的使用方式一样,但它有一个简写的语法,使得它们比标准内部类定义更简洁。

在本文中,您将了解如何在各种情形下使用 lambda 表达式,还将了解 Java 语言 interface 定义的相关扩展。

Java 终于有 Lambda 表达式了~

.NET 关于 Lambda 表达式以及函数委托等的实现,从 2008 年开始,经历了一个技术的演化过程,参看“没有 Lambda 演算何来匿名函数——匿名函数(匿名方法和Lambda)、委托、LINQ”和“.NET C# 声明、实例化和使用委托以及委托在 C# 中的发展”~

了解 lambdas

Lambda 表达式始终是 Java 8 称为函数式接口 的一个对象的实现:定义单一抽象方法的 interface 类。对单一抽象方法的限制很重要,因为 lambda 表达式语法不使用方法名。相反,该表达式使用了 duck typing(匹配参数和返回类型,就像在许多动态语言中所做的那样)来确保所提供的 lambda 与预期的接口方法相兼容。

duck typing,鸭子类型,James Whitcomb Riley 提出的一个著名论断,“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

在清单 1 的示例中,使用了 lambda 来对 Name 实例进行排序。main() 方法中的第一个代码块使用了一个匿名内部类来实现 Comparator<Name> 接口,第二个代码块使用了 lambda 表达式。

清单 1. 匿名内部类与 Lambda 表达式的对比

public class Name {
    public final String firstName;
    public final String lastName;
 
    public Name(String first, String last) {
        firstName = first;
        lastName = last;
    }
 
    // only needed for chained comparator
    public String getFirstName() {
        return firstName;
    }
 
    // only needed for chained comparator
    public String getLastName() {
        return lastName;
    }
 
    // only needed for direct comparator (not for chained comparator)
    public int compareTo(Name other) {
        int diff = lastName.compareTo(other.lastName);
        if (diff == 0) {
            diff = firstName.compareTo(other.firstName);
        }
        return diff;
    }
    ...
}
 
public class NameSort {
    
    PRivate static final Name[] NAMES = new Name[] {
        new Name("Sally", "Smith"),
        ...
    };
    
    private static void printNames(String caption, Name[] names) {
        ...
    }
 
    public static void main(String[] args) {
 
        // sort array using anonymous inner class
        Name[] copy = Arrays.copyOf(NAMES, NAMES.length);
        Arrays.sort(copy, new Comparator<Name>() {
            @Override
            public int compare(Name a, Name b) {
                return a.compareTo(b);
            }
        });
        printNames("Names sorted with anonymous inner class:", copy);
 
        // sort array using lambda expression
        copy = Arrays.copyOf(NAMES, NAMES.length);
        Arrays.sort(copy, (a, b) -> a.compareTo(b));
        printNames("Names sorted with lambda expression:", copy);
        ...
    }
}

在 清单 1 中,lambda 用于替代一个惯用匿名内部类。这种惯用内部类在实践中都很常见,因此 lambda 表达式立即赢得了 Java 8 程序员的器重。(在本例中,内部类和 lambda 都使用在 Name 类中实现的一个方法来执行比较工作。如果 compareTo() 方法代码内联在 lambda 中,那么表达式就不怎么简洁了。)

标准函数式接口

新的 java.util.function 包定义旨在使用 lambdas 的广泛函数式接口。这些接口分为几大类:

  • Function:接受一个参数,基于参数值返回结果
  • Predicate:接受一个参数,基于参数值返回一个布尔值
  • BiFunction:接受两个参数,基于参数值返回结果
  • Supplier:不接受参数,返回一个结果
  • Consumer:接受一个参数,无结果 (void)

这些类别中大部分都包含几个用于处理基本原始参数或返回类型的变量。许多接口定义可用于组合实例的方法,如清单 2 中所示。

清单 2. 组合谓词

// 使用谓词组合删除匹配的名称
List<Name> list = new ArrayList<>();
for (Name name : NAMES) {
    list.add(name);
}
Predicate<Name> pred1 = name -> "Sally".equals(name.firstName);
Predicate<Name> pred2 = name -> "Queue".equals(name.lastName);
list.removeIf(pred1.or(pred2));
printNames("Names filtered by predicate:", list.toArray(new Name[list.size()]));

清单 2 中的代码定义了一对 Predicate<Name>,一个与名 Sally 匹配,第二个与姓 Queue 匹配。pred1.or(pred2) 方法调用构建所定义的组合谓词,方法是依次应用

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表