《JAVA8开发指南》第二章采用Lambda表达式(一)

    xiaoxiao2024-06-06  23

    采用Lambda表达式

    本章,你将学习到如何采用JAVA8的重要特性Lambda表达式。首先,你要了解“行为参数”这种模式。该模式能够使你写出来的代码适应需求变化。然后,你将看到该模式如何使得Lambda表达式的使用与以往比变得更加简洁。然后,你将学习如何精确地定位Lambda表达式的使用场景和使用方式。你也将了解JAVA8的另一个特性-方法参数,它能使你的代码更简洁更易读。带着所有这些新知识实战一个重构代码的例子。最后,你也将学习到如何使用Lambda表达式和方法参数。

    为什么使用Lambda表达式

    将Lambda表达式引入JAVA中的动机源于一个叫“行为参数”的模式。这种模式能够解决需求变化带来的问题,使代码变得更加灵活。在JAVA8之前,参数模式十分啰嗦。Lambda表达式通过精简的方式使用行为模式克服了这个缺点。举个例子,如果你需要找大于一定金额的发票。你可能写这样一个indInvoicesGreaterThanAmount方法:

    List findInvoicesGreaterThanAmount(List invoi ces, double amount) { List result = new ArrayList<>(); for(Invoice inv: invoices) { if(inv.getAmount() > amount) { result.add(inv); } } return result; }

    这个方法确实足够简单。可是如果你还需要找小于一定金额的发票呢?或者更糟糕的是,你需要从一个指定商户中找发票,并且也需要找一定数额呢?这时你需要一个方式来参数化指定条件下的过滤行为。下面我们将定义一个接口InvoicePredicate来描述条件,使用该接口重构上面的方法。

    接口定义 interface InvoicePredicate {

    boolean test(invoice inv);

    }

    重构方法

    List<Invoice> findInvoices(List<Invoice> invoices, InvoicePredi

    cate p) {

    List<Invoice> result = new ArrayList<>();

    for(Invoice inv: invoices) {

    if(p.test(inv)) {

    result.add(inv);

    }

    }

    return result;

    }

     

    使用这段代码,你能够通过增加一个Invoice对象解决需求变化带来的问题。你只需要创建一个不同的InvoicePredicate 对象,将其传入方法findInvoices中。换句话说,你已经参数化了findInvoices行为。不好的是,使用这个新方法引入了额外的冗余,来看下面的代码:

     

     

    List<Invoice> expensiveInvoicesFromOracle

    = findInvoices(invoices, new InvoicePredicate() {

    public test(Invoice inv) {

    return inv.getAmount() > 10_000

    && inv.getCustomer() == Customer.ORACLE;

    }

    });

     

    换句话说,代码变得更灵活的同时可读性变差了。最理想的状态是,代码灵活性和可读性兼备。Lambda表达式的引入能够做到这一点。通过使用它重构上面的代码如下:

     

     

    List<Invoice> expensiveInvoicesFromOracle

    = findInvoices(invoices, inv ->

    inv.getAmount() > 10_000

    && inv.getCustomer() ==

    Customer.ORACLE);

    Lambda表达式的定义

     

    现在知道了为什么需要在代码中引入Lambda表达式,也是时候精准的了解下Lambda表达式的定义。简单的来讲,lambda表达式是一个能够被传递的匿名函数,我们仔细看看这个定义:

     

    匿名

    Lambda表达式是匿名的,因为它没有像普通方法那样有一个明确的名称。它有点儿像匿名类,因为两者都没有明确的名称。

     

    函数

    一个 Lambda表达式像一个方法,它包含一串参数、一个体、一个返回类型和一串可能抛出的错误。和方法不同的是,它没有被声明为特殊类的一部分。

     

    传递

    一个 Lambda表达式能够作为一个方法的参数被传递,也能够作为一个结果被返回。

    Lambda表达式语法

    在写lambda表达式之前,需要知道它的语法。在本书中已经出现过lambda表达式:

    Runnable r = () -> System.out.println("Hi");

    FileFilter isXml = (File f) -> f.getName().endsWith(".xml");

    这两个lambda表达式有三个部分:

    一串参数,比如 (File f)一个有两个字符组成的箭头:- 和 >一个体,比如f.getName().endsWith(".xml")

    lambda表达式有两种形式。当你的lambda表达式体中包含一个语句的时候可以采用第一种:

    (parameters) -> expression

    当你的lambda表达式体中包含一个或多个语句的时候可以使用第二种形。式请注意,你必须使用大括号将表达式包含进来:

    (parameters) -> { statements;}

    大体来说,如果lambda表达式参数的类型被间接的指出过,可以去掉类型声明。另外,如果参数个数只是一个,那么圆括号也可以被去掉。

     

    Lambda表达式使用场景

    现在,了解了如何写一个lambda表达式,接下来的问题是考虑使用lambda表达式的使用方式和使用场景。简单的说,你可以在函数接口中使用lambda表达式。函数接口里包含一个抽象方法。比如上文的两个lambda表达式。

    Runnable r = () -> System.out.println("Hi");

    FileFilter isXml = (File f) -> f.getName().endsWith(".xml"); Runnable就是一个函数接口,因为它包含了一个抽象方法run。FileFilter也是一个函数接口,因为它也定义了一个抽象方法叫accept。

     

     

    @FunctionalInterface

    public interface Runnable {

    void run();

    }

     

    @FunctionalInterface

    public interface FileFilter {

    boolean accept(File pathname);

    lambda表达式重要的一点就是让你创建函数接口的实例。lambda表达式的体提供了函数接口中单个抽象方法的实现。因此,下面使用匿名类和lamba表达式实现的Runnable的结果是一直的。

     

    Runnable r1 = new Runnable() {

    public void run() {

    System.out.println("Hi!");

    }

    };

    r1.run();

     

    Runnable r2 = () -> System.out.println("Hi!");

    r2.run();

    转载自 并发编程网 - ifeve.com

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)