《Java核心技术卷Ⅰ》P242
概述
为什么要学函数式编程?
- 易于使用并发编程,大数据量下,集合处理效率高:可以使用并行流,自动使用多线程方式处理。
- 代码可读性高
- 消灭嵌套地狱s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| List<Author> authors = new ArrayList<>(); List<Book> bookList = new ArrayList<>(); Set<Book> uniqueBookValues = new HashSet<>(); Set<Author> uniqueAuthorValues = new HashSet<>();
for (Author author : authors) { if (uniqueAuthorValues.add(author)) { if (author.getAge() < 18) { List<Book> books = author.getBooks(); for (Book book : books) { if (book.getScore() > 70) { if (uniqueBookValues.add(book)) { bookList.add(book); } } } } } }
System.out.println(bookList);
|
但如果改用函数式编程,代码会变得非常简单:
1 2 3 4 5 6 7 8 9 10 11
| List<Author> authors = new ArrayList<>();
List<Book> collect = authors.stream() .distinct() .filter(author -> author.getAge() < 18) .map(author -> author.getBooks()) .flatMap(Collection::stream) .filter(book -> book.getScore() > 70) .distinct() .collect(Collectors.toList()); System.out.println(collect);
|
函数式编程思想
面向对象思想需要关注用什么对象完成什么事情。而函数式编程思想就类似于我们数学中的函数。它主要关注的是对数据进行了什么操
作。
优点:
- 代码简洁,开发快速
- 接近自然语言,易于理解
- 易于”并发编程”
Lambda表达式
Lambda是JDK8中一个语法糖。它可以对某些匿名内部类的写法进行简化。它是函数式编程思想的一个重要体现,让我们不用关注是什
么对象,而是更关注我们对数据进行了什么操作。
核心原则:
可推导可省略。(如果一些参数的类型可以被推导出来,那么就可以省略它的类型;如果它的方法名可以被推导出来,就可以省略方法名)
格式:
(参数列表)->{代码}
例题
例一:
我们在创建线程并启动时可以使用匿名内部类的写法:
1 2 3 4 5 6
| new Thread(new Runnable(){ @Override public void run(){ System.out.println("你知道吗 我比你想象的 更想在你身边"); } }).start();
|
可以使用Lambda的格式对其进行修改(因为Runnable中只有一个抽象方法需要重写,就能够推出),修改后如下:
1 2 3
| new Thread(()->{ System.out.println("你知道吗 我比你想象的 更想在你身边"); }).start();
|
例二:
现有方法定义如下,其中IntBinaryOperator是一个接口,先使用匿名内部类的写法调用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static int calculateNum(IntBinaryOperator operator){ int a = 10; int b = 20; return operator.applyAsInt(a,b); }
public static void main(string[] args){ int num = calculateNum(new IntBinaryOperator() { @Override public int applyAsInt(int left, int right) { return left + right; } }); System.out.println(num); }
|
小技巧:
写完匿名内部类之后,将鼠标移动到该类上,按下alt + enter
,如果出现了如图所示的情况,表明可以简化成lambda表达式。
(或者看这个类名是不是淡灰色)

Lambda写法:
1 2 3 4 5 6 7 8
| public static void main(string[] args){ int num = calculateNum((int left, int right) -> { return left + right; }); System.out.println(num); }
|
例三:
现有方法定义如下,其中IntPredicate是一个接口,先使用匿名内部类的写法调用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void printNum(IntPredicate predicate){ int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (int i : arr) { if(predicate.test(i)){ System.out.println(i); } } }
public static void main(Strings[] args) { printNum(new IntPredicate() { @Override public boolean test(int value) { return value % 2 == 0; } }) }
|
Lambda写法:
1 2 3 4 5 6
| public static void main(Strings[] args) { printNum((int value)->{ return value % 2 == 0; }); }
|
例四:
现有方法定义如下,其中Function是一个接口。先使用匿名内部类的写法调用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static <R> R typeConver(Function<String, R> function) { String str = "1235"; R result = function.apply(str); return result; }
public static void main(Strings[] args) { Integer reslut = typeConver(new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.valueOf(s); } }); System.out.println("reslut = " + reslut);
String s = typeConver(new Function<String, String>() { @Override public String apply(String s) { return s + "torch"; } }); System.out.println(s); }
|
Lambda写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(Strings[] args) { Integer reslut = typeConver((String s) -> { return Integer.valueof(s); }); System.out.println("reslut = " + reslut); String s = typeConver((String s1) -> { return s1 + "torch"; }); System.out.println(s); }
|
注:不管方法多复杂,牢记关注的是参数类型和方法体,() -> {}, 把参数类型填到()中,把方法体填到{}即可。
例五:
现有方法定义如下,其中IntConsumer是一个接口,先使用匿名内部类的写法调用该方法。
1 2 3 4 5 6
| public static void foreachArr(IntConsumer consumer){ int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(int i : arr) { consumer.accept(i); } }
|
Lambda写法:
省略规则
·参数类型可以省略
·方法体只有一句代码时大括号return和唯-
句代码的分号可以省略
·方法只有一个参数时小括号可以省略
·以上这些规则都记不住也可以省略不记