-
JAVA lambda表达式
2019-09-19 16:01:25JAVA 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:02Java Lambda表达式简介及入门 Java Lambda表达式之方法引用展开全文 -
java lambda表达式
2019-05-23 21:14:00java lambda表达式 此教程是自己听的一个网上的视频,然后做了一下记录,由于注释做的记录很详细,所以注释是主要部分 由于最近想看jdk8源码进行学习,就先把新特性lambda表达式了解了一下 一、首先了解什么是...java lambda表达式
此教程是自己听的一个网上的视频,然后做了一下记录,由于注释做的记录很详细,所以注释是主要部分
由于最近想看jdk8源码进行学习,就先把新特性lambda表达式了解了一下
一、首先了解什么是lambda表达式
二、由于Lambda表达式主要是对接口的简单实现,所以准备一些参数和返回类型不同的情况的接口
下边是具有返回值和具有一个参数要实现的方法的接口,不要去管方法名称,然后再写一些比如没有参数,无返回值的方法的接口
三、表达式基础语法
下边就是对接口的简化实现
四、表达式语法精简
五、lambda方法引用
六、构造方法引用
七:综合练习
集合排序
treeset
foreach
removeif
线程实例化
系统内置函数式接口(这样我们就不需要自己去写接口了)
关于闭包
-
Java Lambda 表达式
2019-02-21 10:28:08Java 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 Lambdas和单一方法接口
-
Java lambda表达式
2019-08-24 21:35:26Java lambda表达式 lambda表达式(JDK8之后推出的特性) 可以使用更简短的方式创建匿名内部类。该语法使得我们可以以“函数式编程”。 创建内部类时实现的接口必须只有一个抽象方法,否则不可以使用类方需要有@... -
Java进阶(五十五)-Java Lambda表达式入门
2017-03-18 09:09:49Java进阶(五十五)-Java Lambda表达式入门 原文链接: Start Using Java Lambda Expressions 下载示例程序 Examples.zip 。 简介 (译者注:虽然看着很先进,其实Lambda表达式的本质只是一个”语法糖”,由... -
lambda表达式java_Java Lambda表达式
2020-07-13 22:34:52lambda表达式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:47Lambda表达式只能用来简化仅包含一个public方法的接口的创建 只能是接口 否则报:Target type of a lambda conversion must be an interface 只能有一个public方法 否则报:Multiple non-overriding abstract ... -
Java lambda 表达式
2018-12-10 23:47:101、什么是lamdba表达式: 很多年前,逻辑学家Alonzo Church想要形式化地表示能有效计算的数学函数。他使用了希腊字母lambda(Λ)来标记参数,实际上,权威的《数学原理...2、Java为什么要引入lambda表达式: java在... -
JAVA Lambda表达式
2017-12-07 14:30:41在JAVA中,“Lambda 表达式”(Lambda expression)是一个抽象方法的实现...Lambda表达式就是函数式接口的实现,来完成对数据的处理。 基本函数的格式 JAVA函数通常是我们类中定义的行为,格式如下: 修饰符 返回类型... -
java lambda表达式_《Java》Lambda表达式
2020-11-29 09:16:09思路决定出路,格局决定结局1、简介lambda表达式,是一种简写代码的方式,去掉一些没有意义的代码,只留下核心逻辑,让代码看起来更加紧凑,是推动Java 8发布的新特性2、特征2.1、可以使用Lambda表达式的前提是:一...
-
MaxScale 实现 MySQL 读写分离与负载均衡
-
井字游戏-源码
-
基于Qt的LibVLC开发教程
-
2021Android复习找工作进大厂冲冲冲------开篇
-
MySQL Router 实现高可用、负载均衡、读写分离
-
iOS开发之Objective-C与JavaScript的交互
-
程序员必修基础套餐课
-
Vuex的State核心概念
-
LVS + Keepalived 实现 MySQL 负载均衡与高可用
-
CsLiB
-
guidoman.github.io-源码
-
数学建模A乘车难问题.zip
-
相位调制非线性对开环光纤陀螺工作点测量与信号解调的影响
-
18-Redis高级
-
基于python的dango框架购物商城毕业设计毕设源代码使用教程
-
query条件查询
-
Study on digital holography with single phase-shifting operation
-
Galera 高可用 MySQL 集群(PXC v5.7+Hapro)
-
敏捷软件开发和极限编程介绍
-
高中地理湘教版必修二人口迁移.docx