精华内容
下载资源
问答
  • JAVA lambda表达式

    2019-09-19 16:01:25
    JAVA lambda表达式
  • Java Lambda表达式入门

    万次阅读 多人点赞 2014-04-27 21:17:58
    原文链接:Start Using Java ...(译者认为: 超过3行的逻辑就不适用Lambda表达式了。虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用...

    原文链接: Start Using Java Lambda Expressions

    下载示例程序 Examples.zip
    原文日期: 2014年4月16日

    翻译日期: 2014年4月27日
    翻译人员: 铁锚
    简介

    (译者认为: 超过3行的逻辑就不适用Lambda表达式了。虽然看着很先进,其实Lambda表达式的本质只是一个"语法糖",由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能。本人建议不要乱用,因为这就和某些很高级的黑客写的代码一样,简洁,难懂,难以调试,维护人员想骂娘.)
    Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。
    Lambda表达式还增强了集合库。 Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包。 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。 总的来说,lambda表达式和 stream 是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。 在本文中,我们将从简单到复杂的示例中见认识lambda表达式和stream的强悍。
    环境准备
    如果还没有安装Java 8,那么你应该先安装才能使用lambda和stream(译者建议在虚拟机中安装,测试使用)。 像NetBeans 和IntelliJ IDEA 一类的工具和IDE就支持Java 8特性,包括lambda表达式,可重复的注解,紧凑的概要文件和其他特性。
    下面是Java SE 8和NetBeans IDE 8的下载链接:
    Java Platform (JDK 8): 从Oracle下载Java 8,也可以和NetBeans IDE一起下载
    NetBeans IDE 8: 从NetBeans官网下载NetBeans IDE
    Lambda表达式的语法
    基本语法:
    (parameters) -> expression

    (parameters) ->{ statements; }

    下面是Java lambda表达式的简单例子:

    // 1. 不需要参数,返回值为 5
    () -> 5
    
    // 2. 接收一个参数(数字类型),返回其2倍的值
    x -> 2 * x
    
    // 3. 接受2个参数(数字),并返回他们的差值
    (x, y) -> x – y
    
    // 4. 接收2个int型整数,返回他们的和
    (int x, int y) -> x + y
    
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
    (String s) -> System.out.print(s)

    基本的Lambda例子
    现在,我们已经知道什么是lambda表达式,让我们先从一些基本的例子开始。 在本节中,我们将看到lambda表达式如何影响我们编码的方式。 假设有一个玩家List ,程序员可以使用 for 语句 ("for 循环")来遍历,在Java SE 8中可以转换为另一种形式:

     

    String[] atp = {"Rafael Nadal", "Novak Djokovic",
           "Stanislas Wawrinka",
           "David Ferrer","Roger Federer",
           "Andy Murray","Tomas Berdych",
           "Juan Martin Del Potro"};
    List<String> players =  Arrays.asList(atp);
    
    // 以前的循环方式
    for (String player : players) {
         System.out.print(player + "; ");
    }
    
    // 使用 lambda 表达式以及函数操作(functional operation)
    players.forEach((player) -> System.out.print(player + "; "));
     
    // 在 Java 8 中使用双冒号操作符(double colon operator)
    players.forEach(System.out::println);

    正如您看到的,lambda表达式可以将我们的代码缩减到一行。 另一个例子是在图形用户界面程序中,匿名类可以使用lambda表达式来代替。 同样,在实现Runnable接口时也可以这样使用:

     

    // 使用匿名内部类
    btn.setOnAction(new EventHandler<ActionEvent>() {
              @Override
              public void handle(ActionEvent event) {
                  System.out.println("Hello World!"); 
              }
        });
     
    // 或者使用 lambda expression
    btn.setOnAction(event -> System.out.println("Hello World!"));
    

    下面是使用lambdas 来实现 Runnable接口 的示例:

     

    // 1.1使用匿名内部类
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello world !");
        }
    }).start();
    
    // 1.2使用 lambda expression
    new Thread(() -> System.out.println("Hello world !")).start();
    
    // 2.1使用匿名内部类
    Runnable race1 = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello world !");
        }
    };
    
    // 2.2使用 lambda expression
    Runnable race2 = () -> System.out.println("Hello world !");
     
    // 直接调用 run 方法(没开新线程哦!)
    race1.run();
    race2.run();


    Runnable 的 lambda表达式,使用块格式,将五行代码转换成单行语句。 接下来,在下一节中我们将使用lambdas对集合进行排序。
    使用Lambdas排序集合
    在Java中,Comparator 类被用来排序集合。 在下面的例子中,我们将根据球员的 name, surname, name 长度 以及最后一个字母。 和前面的示例一样,先使用匿名内部类来排序,然后再使用lambda表达式精简我们的代码。
    在第一个例子中,我们将根据name来排序list。 使用旧的方式,代码如下所示:

     

    String[] players = {"Rafael Nadal", "Novak Djokovic", 
        "Stanislas Wawrinka", "David Ferrer",
        "Roger Federer", "Andy Murray",
        "Tomas Berdych", "Juan Martin Del Potro",
        "Richard Gasquet", "John Isner"};
     
    // 1.1 使用匿名内部类根据 name 排序 players
    Arrays.sort(players, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return (s1.compareTo(s2));
        }
    });

    使用lambdas,可以通过下面的代码实现同样的功能:

     

    // 1.2 使用 lambda expression 排序 players
    Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
    Arrays.sort(players, sortByName);
    
    // 1.3 也可以采用如下形式:
    Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));


    其他的排序如下所示。 和上面的示例一样,代码分别通过匿名内部类和一些lambda表达式来实现Comparator :

     

    // 1.1 使用匿名内部类根据 surname 排序 players
    Arrays.sort(players, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
        }
    });
    
    // 1.2 使用 lambda expression 排序,根据 surname
    Comparator<String> sortBySurname = (String s1, String s2) -> 
        ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
    Arrays.sort(players, sortBySurname);
    
    // 1.3 或者这样,怀疑原作者是不是想错了,括号好多...
    Arrays.sort(players, (String s1, String s2) -> 
          ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) 
        );
    
    // 2.1 使用匿名内部类根据 name lenght 排序 players
    Arrays.sort(players, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return (s1.length() - s2.length());
        }
    });
    
    // 2.2 使用 lambda expression 排序,根据 name lenght
    Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
    Arrays.sort(players, sortByNameLenght);
    
    // 2.3 or this
    Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
    
    // 3.1 使用匿名内部类排序 players, 根据最后一个字母
    Arrays.sort(players, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
        }
    });
    
    // 3.2 使用 lambda expression 排序,根据最后一个字母
    Comparator<String> sortByLastLetter = 
        (String s1, String s2) -> 
            (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
    Arrays.sort(players, sortByLastLetter);
    
    // 3.3 or this
    Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
    

    就是这样,简洁又直观。 在下一节中我们将探索更多lambdas的能力,并将其与 stream 结合起来使用。
    使用Lambdas和Streams
    Stream是对集合的包装,通常和lambda一起使用。 使用lambdas可以支持许多操作,如 map, filter, limit, sorted, count, min, max, sum, collect 等等。 同样,Stream使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst() 这样的方法就会结束链式语法。 在接下来的例子中,我们将探索lambdas和streams 能做什么。 我们创建了一个Person类并使用这个类来添加一些数据到list中,将用于进一步流操作。 Person 只是一个简单的POJO类:

     

    public class Person {
    
    private String firstName, lastName, job, gender;
    private int salary, age;
    
    public Person(String firstName, String lastName, String job,
                    String gender, int age, int salary)       {
              this.firstName = firstName;
              this.lastName = lastName;
              this.gender = gender;
              this.age = age;
              this.job = job;
              this.salary = salary;
    }
    // Getter and Setter 
    // . . . . .
    }

    接下来,我们将创建两个list,都用来存放Person对象:

     

    List<Person> javaProgrammers = new ArrayList<Person>() {
      {
        add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
        add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
        add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
        add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
        add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
        add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
        add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));
        add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));
        add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));
        add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));
      }
    };
    
    List<Person> phpProgrammers = new ArrayList<Person>() {
      {
        add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
        add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
        add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
        add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000));
        add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
        add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300));
        add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
        add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000));
        add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
        add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800));
      }
    };

    现在我们使用forEach方法来迭代输出上述列表:

     

    System.out.println("所有程序员的姓名:");
    javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
    phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

    我们同样使用forEach方法,增加程序员的工资5%:

     

    System.out.println("给程序员加薪 5% :");
    Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
    
    javaProgrammers.forEach(giveRaise);
    phpProgrammers.forEach(giveRaise);

    另一个有用的方法是过滤器filter() ,让我们显示月薪超过1400美元的PHP程序员:

     

    System.out.println("下面是月薪超过 $1,400 的PHP程序员:")
    phpProgrammers.stream()
              .filter((p) -> (p.getSalary() > 1400))
              .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

    我们也可以定义过滤器,然后重用它们来执行其他操作:

     

    // 定义 filters
    Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
    Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
    Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));
    
    System.out.println("下面是年龄大于 24岁且月薪在$1,400以上的女PHP程序员:");
    phpProgrammers.stream()
              .filter(ageFilter)
              .filter(salaryFilter)
              .filter(genderFilter)
              .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
    
    // 重用filters
    System.out.println("年龄大于 24岁的女性 Java programmers:");
    javaProgrammers.stream()
              .filter(ageFilter)
              .filter(genderFilter)
              .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
    

    使用limit方法,可以限制结果集的个数:

     

    System.out.println("最前面的3个 Java programmers:");
    javaProgrammers.stream()
              .limit(3)
              .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
    
    
    System.out.println("最前面的3个女性 Java programmers:");
    javaProgrammers.stream()
              .filter(genderFilter)
              .limit(3)
              .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

    排序呢? 我们在stream中能处理吗? 答案是肯定的。 在下面的例子中,我们将根据名字和薪水排序Java程序员,放到一个list中,然后显示列表:

    // 静态引入

    import static java.util.stream.Collectors.toList;

     

    System.out.println("根据 name 排序,并显示前5个 Java programmers:");
    List<Person> sortedJavaProgrammers = javaProgrammers
              .stream()
              .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
              .limit(5)
              .collect(toList());
    
    sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
     
    System.out.println("根据 salary 排序 Java programmers:");
    sortedJavaProgrammers = javaProgrammers
              .stream()
              .sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
              .collect( toList() );
    
    sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));

    如果我们只对最低和最高的薪水感兴趣,比排序后选择第一个/最后一个 更快的是min和max方法:

     

    System.out.println("工资最低的 Java programmer:");
    Person pers = javaProgrammers
              .stream()
              .min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
              .get()
    
    System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
    
    System.out.println("工资最高的 Java programmer:");
    Person person = javaProgrammers
              .stream()
              .max((p, p2) -> (p.getSalary() - p2.getSalary()))
              .get()
    
    System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())

    上面的例子中我们已经看到 collect 方法是如何工作的。 结合 map 方法,我们可以使用 collect 方法来将我们的结果集放到一个字符串,一个 Set 或一个TreeSet中:

     

    System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
    String phpDevelopers = phpProgrammers
              .stream()
              .map(Person::getFirstName)
              .collect(joining(" ; ")); // 在进一步的操作中可以作为标记(token)   
    
    System.out.println("将 Java programmers 的 first name 存放到 Set:");
    Set<String> javaDevFirstName = javaProgrammers
              .stream()
              .map(Person::getFirstName)
              .collect(toSet());
    
    System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
    TreeSet<String> javaDevLastName = javaProgrammers
              .stream()
              .map(Person::getLastName)
              .collect(toCollection(TreeSet::new));
    

    Streams 还可以是并行的(parallel)。 示例如下:

     

    System.out.println("计算付给 Java programmers 的所有money:");
    int totalSalary = javaProgrammers
              .parallelStream()
              .mapToInt(p -> p.getSalary())
              .sum();

    我们可以使用summaryStatistics方法获得stream 中元素的各种汇总数据。 接下来,我们可以访问这些方法,比如getMax, getMin, getSum或getAverage:

     

    //计算 count, min, max, sum, and average for numbers
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    IntSummaryStatistics stats = numbers
              .stream()
              .mapToInt((x) -> x)
              .summaryStatistics();
    
    System.out.println("List中最大的数字 : " + stats.getMax());
    System.out.println("List中最小的数字 : " + stats.getMin());
    System.out.println("所有数字的总和   : " + stats.getSum());
    System.out.println("所有数字的平均值 : " + stats.getAverage()); 

    OK,就这样,希望你喜欢它!
    总结
    在本文中,我们学会了使用lambda表达式的不同方式,从基本的示例,到使用lambdas和streams的复杂示例。 此外,我们还学习了如何使用lambda表达式与Comparator 类来对Java集合进行排序。

    展开全文
  • Java Lambda表达式

    2020-02-06 23:33:02
  • java lambda表达式

    2019-05-23 21:14:00
    java lambda表达式 此教程是自己听的一个网上的视频,然后做了一下记录,由于注释做的记录很详细,所以注释是主要部分 由于最近想看jdk8源码进行学习,就先把新特性lambda表达式了解了一下 一、首先了解什么是...

    java lambda表达式

    此教程是自己听的一个网上的视频,然后做了一下记录,由于注释做的记录很详细,所以注释是主要部分

    由于最近想看jdk8源码进行学习,就先把新特性lambda表达式了解了一下

    一、首先了解什么是lambda表达式

    二、由于Lambda表达式主要是对接口的简单实现,所以准备一些参数和返回类型不同的情况的接口

    下边是具有返回值和具有一个参数要实现的方法的接口,不要去管方法名称,然后再写一些比如没有参数,无返回值的方法的接口

    三、表达式基础语法

    下边就是对接口的简化实现

    四、表达式语法精简

    五、lambda方法引用

    六、构造方法引用

     

    七:综合练习

    集合排序

    treeset

     

    foreach

     

    removeif

    线程实例化

     

    系统内置函数式接口(这样我们就不需要自己去写接口了)

      关于闭包

    展开全文
  • Java Lambda 表达式

    2019-02-21 10:28:08
    Java Lambda 表达式 [翻译]原文链接http://tutorials.jenkov.com/java/lambda-expressions.html#single-method-interface 如有侵权,请告知。...Lambda表达式与匿名接口实现 Lambda类型推断 Lambda...

    Java Lambda 表达式

    [翻译]原文链接http://tutorials.jenkov.com/java/lambda-expressions.html#single-method-interface
    如有侵权,请告知。

    • Java Lambdas和单一方法接口
      • 匹配Lambda到接口
      • 具有默认和静态方法的接口
    • Lambda表达式与匿名接口实现
    • Lambda类型推断
    • Lambda参数
      • 零参数
      • 一个参数
      • 多个参数
      • 参数类型
      • 来自Java 11的var参数类型
    • Lambda函数
    • 从Lambda表达式返回值
    • Lambdas作为对象
    • 访问变量
      • 访问局部变量
      • 访问实例变量
      • 访问静态变量
    • 方法引用
      • 静态方法引用
      • 参数方法引用
      • 实例方法引用
      • 构造函数引用

    Java lambda表达式是Java 8中的新增功能.Java lambda表达式是Java进入函数式编程的第一步。
    因此,Java lambda表达式是一个可以在不属于任何类的情况下创建的函数。
    Java lambda表达式可以作为对象传递,并按需执行。
    Java lambda表达式通常用于实现简单的事件侦听器/回调,或者使用Java Streams API进行函数式编程

    Java Lambdas和单一方法接口

    函数式编程经常用于实现事件侦听器。Java中的事件侦听器通常使用单个方法定义为Java接口。这是一个假设的单一方法接口示例:

    public interface StateChangeListener {
    
        public void onStateChange(State oldState, State newState);
    
    }
    

    此Java接口定义了一个方法,只要状态发生变化(无论观察到什么),就会调用该方法。在Java 7中,您必须实现此接口才能监听状态更改。想象一下,你有一个名为StateOwner的类,它可以注册状态事件监听器。这是一个例子:

    public class StateOwner {
    
        public void addStateListener(StateChangeListener listener) { ... }
    
    }
    

    在Java7可以使用一个匿名内部接口实现类添加监听。就像下面

    StateOwner stateOwner = new StateOwner();
    
    stateOwner.addStateListener(new StateChangeListener() {
    
        public void onStateChange(State oldState, State newState) {
            // do something with the old and new state.
        }
    });
    

    首先创建一个StateOwner实例。然后在StateOwner实例上添加StateChangeListener接口的匿名实现作为侦听器。在Java 8中,您可以使用Java lambda表达式添加事件侦听器,如下所示:

    StateOwner stateOwner = new StateOwner();
    
    stateOwner.addStateListener(
        (oldState, newState) -> System.out.println("State changed")
    );
    

    lambda表达式是这一部分:

    (oldState, newState) -> System.out.println("State changed")
    

    lambda表达式与addStateListener()方法的参数的参数类型匹配。如果lambda表达式与参数类型(在本例中为StateChangeListener接口)匹配,则lambda表达式将转换为实现与该参数相同的接口的函数。Java lambda表达式只能在与它们匹配的类型是单个方法接口的情况下使用。在上面的示例中,lambda表达式用作参数,其中参数类型是StateChangeListener接口。该接口只有一个方法。因此,lambda表达式与该接口成功匹配。

    具有默认方法与静态方法的接口

    从Java 8开始,Java接口可以包含默认方法和静态方法。默认方法和静态方法都有直接在接口声明中定义的实现。这意味着,Java lambda表达式可以实现具有多个方法的接口 - 只要接口只有一个未实现的(AKA抽象)方法。换句话说,只要接口只包含一个未实现的(抽象)方法,接口仍然是一个功能接口,即使它包含默认和静态方法。
    可以使用lambda表达式实现以下接口:

    import java.io.IOException;
    import java.io.OutputStream;
    
    public interface MyInterface {
    
        void printIt(String text);
    
        default public void printUtf8To(String text, OutputStream outputStream){
            try {
                outputStream.write(text.getBytes("UTF-8"));
            } catch (IOException e) {
                throw new RuntimeException("Error writing String as UTF-8 to OutputStream", e);
            }
        }
    
        static void printItToSystemOut(String text){
            System.out.println(text);
        }
    }
    

    尽管此接口包含3个方法,但它可以通过lambda表达式实现,因为只有一个方法未实现。以下是实现的外观:

    MyInterface myInterface = (String text) -> {
        System.out.print(text);
    };
    

    Lambda表达式与匿名接口实现

    尽管lambda表达式接近于匿名接口实现,但仍有一些值得注意的差异。主要区别在于,匿名接口实现可以具有状态(成员变量),而lambda表达式则不能。看看这个接口:

    public interface MyEventConsumer {
    
        public void consume(Object event);
    
    }
    

    这个接口可以通过一个匿名内部类来实现,比如:

    
    MyEventConsumer consumer = new MyEventConsumer() {
        public void consume(Object event){
            System.out.println(event.toString() + " consumed");
        }
    };
    

    这个匿名内部类MyEventConsumer可以拥有属于自己的局部变量。看一下下面的这个重新设计:

    
    MyEventConsumer myEventConsumer = new MyEventConsumer() {
        private int eventCount = 0;
        public void consume(Object event) {
            System.out.println(event.toString() + " consumed " + this.eventCount++ + " times.");
        }
    };
    

    请注意匿名MyEventConsumer实现现在具有名为eventCount的字段。lambda表达式不能包含这样的字段。因此,lambda表达式被认为是无状态的。

    Lambda类型推断

    在Java 8之前,你必须在进行匿名接口实现时指定要实现的接口。以下是本文开头的匿名接口实现示例:

    stateOwner.addStateListener(new StateChangeListener() {
    
        public void onStateChange(State oldState, State newState) {
            // do something with the old and new state.
        }
    });
    

    对于lambda表达式,通常可以从周遭的代码推断出类型。例如,参数的接口类型可以从addStateListener()方法的方法声明(StateChangeListener接口上的单个方法)推断出来。这称为类型推断。编译器通过查找类型的其他地方来推断参数的类型 - 在本例中是方法定义。以下是本文开头的示例,显示lambda表达式中未提及StateChangeListener接口:

    stateOwner.addStateListener(
        (oldState, newState) -> System.out.println("State changed")
    );
    

    在lambda表达式中,通常也可以推断出参数类型。在上面的示例中,编译器可以从onStateChange()方法声明中推断出它们的类型。因此,参数oldState和newState的类型是从onStateChange()方法的方法声明中推断出来的。

    Lambda参数

    由于Java lambda表达式实际上只是方法,因此lambda表达式可以像方法一样获取参数。前面显示的lambda表达式的(oldState,newState)部分指定了lambda表达式所采用的参数。这些参数必须与单个方法接口上的方法参数匹配。在这种情况下,这些参数必须匹配StateChangeListener接口的onStateChange()方法的参数:

    public void onStateChange(State oldState, State newState);
    

    lambda表达式和方法中的参数数量必须至少匹配。其次,如果在lambda表达式中指定了任何参数类型,则这些类型也必须匹配。我还没有向您展示如何将类型放在lambda表达式参数上(本文稍后将对其进行介绍),但在许多情况下您不需要它们。

    Lambda方法体

    lambda表达式的主体,以及它所代表的函数/方法的主体,被指定在lambda声明中 - >的右侧:这是一个例子:

    (oldState, newState) -> System.out.println("State changed")
    

    如果你的lambda表达式需要由多行组成,你可以将lambda函数体括在{}括号中,Java在其他地方声明方法时也需要这样。这是一个例子:

     (oldState, newState) -> {
        System.out.println("Old state: " + oldState);
        System.out.println("New state: " + newState);
      }
    

    Lambda表达式返回值

    您可以从Java lambda表达式返回值,就像从普通方法中一样。你只需向lambda函数体添加一个return语句,如下所示:

     (param) -> {
        System.out.println("param: " + param);
        return "return value";
      }
    

    如果你的lambda表达式所做的只是计算返回值并返回它,你可以用更短的方式指定返回值。而不是这个:

     (a1, a2) -> { return a1 > a2; }
    

    你可以这样写:

    (a1, a2) -> a1 > a2;
    

    然后编译器会发现表达式a1> a2是lambda表达式的返回值(因此名称为lambda表达式 - 因为表达式返回某种值)。

    Lambda作为对象

    Java lambda表达式本质上是一个对象。您可以将lambda表达式赋给变量并传递它,就像使用任何其他对象一样。这是一个例子:

    public interface MyComparator {
    
        public boolean compare(int a1, int a2);
    
    }
    MyComparator myComparator = (a1, a2) -> return a1 > a2;
    
    boolean result = myComparator.compare(2, 5);
    

    第一个代码块显示了lambda表达式实现的接口。第二个代码块显示了lambda表达式的定义,lambda表达式如何赋给变量,以及最后如何通过调用它实现的接口方法来调用lambda表达式。

    变量访问

    Java lambda表达式能够访问在某些情况下在lambda函数体外声明的变量。
    Java lambdas可以访问以下类型的变量

    • 局部变量
    • 实例变量
    • 静态变量

    访问局部变量

    Java lambda可以捕获在lambda体外声明的局部变量的值。为了说明这一点,首先看一下这个单一的方法接口:

    public interface MyFactory {
        public String create(char[] chars);
    }
    

    现在,看看这个实现MyFactory接口的lambda表达式:

    MyFactory myFactory = (chars) -> {
        return new String(chars);
    };
    

    现在这个lambda表达式只引用传递给它的参数值(字符)。但我们可以改变这一点。这是一个更新版本,引用在lambda函数体外声明的String变量:

    String myString = "Test";
    
    MyFactory myFactory = (chars) -> {
        return myString + ":" + new String(chars);
    };
    

    如您所见,lambda体现在引用在lambda体外声明的局部变量myString。当且仅当引用的变量是“有效最终”时,这是可能的,这意味着它在分配后不会改变其值。如果myString变量稍后更改了它的值,编译器会抱怨lambda函数内部对它的引用。

    访问实例变量

    lambda表达式还可以访问创建lambda的对象中的实例变量。这里有一个示例:

    public class EventConsumerImpl {
    
        private String name = "MyConsumer";
    
        public void attach(MyEventProducer eventProducer){
            eventProducer.listen(e -> {
                System.out.println(this.name);
            });
        }
    }
    

    注意lambda表达式内对this.name的引用。这就是访问封闭的EventConsumerImpl对象的name实例变量。甚至在获取到该变量以后,该变量的值发生变化,,该值同样会在lambda表达式内得到体现。
    this其语义实际上是Java lambdas与接口的匿名实现不同的方面之一。匿名接口实现可以有自己的实例变量,这些变量通过this引用。但是,lambda不能有自己的实例变量,所以这总是指向封闭对象。
    注意:事件消费者的上述设计并不是特别优雅。我这样做只是为了说明关于实例变量的访问。

    访问静态变量

    Java lambda表达式还可以访问静态变量。这并不奇怪,因为静态变量可以从Java应用程序中的任何地方访问,只要可以访问静态变量(packaged或者public修饰的)。
    这是一个创建lambda的示例类,它引用lambda体内部的静态变量:

    public class EventConsumerImpl {
        private static String someStaticVar = "Some text";
    
        public void attach(MyEventProducer eventProducer){
            eventProducer.listen(e -> {
                System.out.println(someStaticVar);
            });
        }
    }
    

    在lambda访问静态变量之后,也允许更改静态变量的值。
    同样,上面的类设计有点荒谬。不要过多考虑这个问题。该类主要用于向您显示lambda可以访问静态变量。

    lambda中关于方法调用

    在所有lambda表达式都是使用传递给lambda的参数调用另一个方法的情况下,Java lambda实现提供了一种表达方法调用的更短方式。首先,这是一个示例单功能接口:

    public interface MyPrinter{
        public void print(String s);
    }
    

    以下是创建实现MyPrinter接口的Java lambda实例的示例:

    MyPrinter myPrinter = (s) -> { System.out.println(s); };
    

    因为lambda体只包含一个语句,所以我们实际上可以省略括号{}括号。此外,由于lambda方法只有一个参数,我们可以省略参数周围的enclosing()括号。以下是简化的lambda声明:

    MyPrinter myPrinter = s -> System.out.println(s);
    

    由于所有lambda body都将字符串参数转发给System.out.println()方法,因此我们可以用方法引用替换上面的lambda声明。以下是lambda方法引用的样式:

    MyPrinter myPrinter = System.out::println;
    

    注意双冒号::。这些符号通知Java编译器这是一个方法引用。引用的方法是双冒号之后的方法。无论哪个类或对象拥有引用的方法都来自双冒号。
    你可以引用以下类型的方法:

    • 静态方法
    • 成员方法
    • 成员方法
    • 构造方法

    引用静态方法

    最简单的引用方法是静态方法。这是第一个单一功能接口的示例:

    public interface Finder {
        public int find(String s1, String s2);
    }
    

    这是一个我们想要创建方法引用的静态方法

    public class MyClass{
        public static int doFind(String s1, String s2){
            return s1.lastIndexOf(s2);
        }
    }
    

    最后这里是一个引用静态方法的Java lambda表达式:

    Finder finder = MyClass::doFind;
    

    由于Finder.find()和MyClass.doFind()方法的参数匹配,因此可以创建一个实现Finder.find()并引用MyClass.doFind()方法的lambda表达式。

    引用有参数的方法

    您还可以将其中一个参数的方法引用到lambda。想象一下单个函数接口,如下所示:

    public interface Finder {
        public int find(String s1, String s2);
    }
    

    该接口旨在表示能够搜索s1以发生s2的组件。下面是一个Java lambda表达式的示例,它调用String.indexOf()来搜索:

    Finder finder = String::indexOf;
    

    等同于以下的lambda表达式

    Finder finder = (s1, s2) -> s1.indexOf(s2);
    

    请注意快捷方式版本如何引用单个方法。Java编译器将尝试将引用的方法与第一个参数类型匹配,使用第二个参数类型作为引用方法的参数。
    ####实例方法引用
    第三,还可以从lambda定义引用实例方法。首先,让我们看一下单个方法接口定义:

    public interface Deserializer {
        public int deserialize(String v1);
    }
    

    此接口表示能够将String“反序列化”为int的组件。现在看看这个StringConverter类

    public class StringConverter {
        public int convertToInt(String v1){
            return Integer.valueOf(v1);
        }
    }
    

    convertToInt()方法与Deserializer deserialize()方法的deserialize()方法具有相同的特征。因此,我们可以创建一个StringConverter实例并从Java lambda表达式引用它的convertToInt()方法,如下所示:

    StringConverter stringConverter = new StringConverter();
    
    Deserializer des = stringConverter::convertToInt;
    

    由两行中的第二行创建的lambda表达式引用在第一行上创建的StringConverter实例的convertToInt方法。

    引用构造方法

    最后,可以引用类的构造函数。您可以通过编写类名后跟:: new来实现,如下所示:

    MyClass::new
    

    要了解如何将构造函数用作lambda表达式,请查看此接口定义:

    public interface Factory {
        public String create(char[] val);
    }
    

    此接口的create()方法与String类中的一个构造函数的签名匹配。因此,此构造函数可用作lambda。以下是该示例的示例:

    Factory factory = String::new;
    

    与以下的lamb表达式等同

    Factory factory = chars -> new String(chars);
    
    展开全文
  • Java lambda表达式

    2019-08-24 21:35:26
    Java lambda表达式 lambda表达式(JDK8之后推出的特性) 可以使用更简短的方式创建匿名内部类。该语法使得我们可以以“函数式编程”。 创建内部类时实现的接口必须只有一个抽象方法,否则不可以使用类方需要有@...
  • Java进阶(五十五)-Java Lambda表达式入门

    万次阅读 多人点赞 2017-03-18 09:09:49
    Java进阶(五十五)-Java Lambda表达式入门  原文链接: Start Using Java Lambda Expressions   下载示例程序 Examples.zip 。 简介  (译者注:虽然看着很先进,其实Lambda表达式的本质只是一个”语法糖”,由...
  • lambda表达式javaJava lambda expression can be considered as one of the coolest feature introduced in Java 8. It is Java’s first step into functional programming. A Java lambda expression can be ...
  • java Lambda表达式

    万次阅读 2019-04-09 16:47:47
    Lambda表达式只能用来简化仅包含一个public方法的接口的创建 只能是接口 否则报:Target type of a lambda conversion must be an interface 只能有一个public方法 否则报:Multiple non-overriding abstract ...
  • Java lambda 表达式

    2018-12-10 23:47:10
    1、什么是lamdba表达式: 很多年前,逻辑学家Alonzo Church想要形式化地表示能有效计算的数学函数。他使用了希腊字母lambda(Λ)来标记参数,实际上,权威的《数学原理...2、Java为什么要引入lambda表达式: java在...
  • JAVA Lambda表达式

    2017-12-07 14:30:41
    JAVA中,“Lambda 表达式”(Lambda expression)是一个抽象方法的实现...Lambda表达式就是函数式接口的实现,来完成对数据的处理。 基本函数的格式 JAVA函数通常是我们类中定义的行为,格式如下: 修饰符 返回类型...
  • 思路决定出路,格局决定结局1、简介lambda表达式,是一种简写代码的方式,去掉一些没有意义的代码,只留下核心逻辑,让代码看起来更加紧凑,是推动Java 8发布的新特性2、特征2.1、可以使用Lambda表达式的前提是:一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,063
精华内容 8,825
关键字:

javalambda表达式

java 订阅