在Java的语言世界里面,除了基本的数据类型,一切都是对象,例如:String字符串、每个对象实例、数组这些都是对象。在Java中方法是不能完全独立存在的,不能将方法作为参数或者返回值给实例。注意:方法(Method),函数(Function),在Java中是没有函数的,因为Java是一个面向对象编程的语言;函数更多的意义是用来表述面向过程的语言;又或者说"函数是大家的函数,方法是类的方法",不过,不用这么纠结,我们可以认为在Java中方法就是函数,函数就是方法。
为什么出现Lambda表达式
从线程的构建、自定义比较器、Swing等等,这些我们都是匿名内部类的方式去书写。为了简化这些代码的书写,使得代码更加紧凑,更为了使得Java拥有函数式编程的特点。javaScript是典型的函数式编程语言,点击这里以及这里了解,函数式语言提供了一种强大的功能–闭包,闭包的特点是词法的作用域与把函数当作值来传递。虽然闭包与Lambda表达式之间存在显著差别,但是Lambda表达式至少是很好的闭包替代者。
Lambda表达式是什么
在Java中,刚开始说过,除了基本的数据类型,一切都是对象,那么Lambda表达式也是对象,所以必须依赖于一种特殊的对象类型–函数式接口(Function Interface)。Lambda表达式可以理解为刚开始说的匿名内部类函数,它没有声明方法,没有访问修饰符,没有返回值声明和名字。
Lambda表达式的特征
- 可选型声明:不需要声明参数类型,编译器可以统一识别参数值;
- 可选的参数圆括号:一个参数且类型可推导时,无需定义圆括号,但是多个参数需要定义圆括号;
- 可选的大括号:如果函数主体包含了一个语句,不需要大括号;
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要显式指定表达式返回值。
Lambda表达式的用法
-
Lambda表达式语法:(parameters)->expression 或 (parameters)->{statements}
Lambda表达式语法解析:parameters:参数 expression:表达式(一条语句) statements:陈述(多个语句)
-
用法
1. 替代匿名内部类
实现Runnable接口是匿名内部类的典型例子
```java
// 代码示例
public void runable() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("the old runnable now is useing");
}
}).start();
new Thread(() -> System.out.println("the new runnable now is useing")).start();
}
```
2. 集合的迭代
使用Lambda表达式对map集合进行迭代遍历
```java
// 代码示例
private void listiterator() {
List<String> languages = Arrays.asList("java", "python", "javaScript");
languages.forEach(x -> System.out.println(x));
}
```
3. 实现map
使用Lambda表达式实现map,map的作用是把一个对象换做另一个,这块涉及到Java8的新特性Stream,下一篇博客会讲。
```java
// 代码示例
private void mapFunction(){
List<Double> languages = Arrays.asList(2.0, 4.0, 6.0);
languages.stream().map(x -> x + x * 0.5).forEach((x) -> System.out.println(x));
}
```
4. 实现reduce
使用Lambda表达式实现reduce,reduce是将所有的值合并为一个,这块涉及到Java8的新特性Stream,下一篇博客会讲。
```java
// 代码示例
private void sumFunction() {
List<Double> cost = Arrays.asList(2.0, 4.0, 6.0);
double doubleCode = cost.stream().reduce((sum, x) -> sum + x).get();
System.out.println(doubleCode);
}
```
5. 过滤操作
使用Lambda表达式实现对集合的过滤,filter过滤掉集合中的部分元素,这块涉及到Java8的新特性Stream,下一篇博客会讲。
```java
// 代码示例
private void filterFunction() {
List<Double> cost = Arrays.asList(2.0, 4.0, 6.0);
//方式一
cost.stream().filter(x -> x > 3).collect(Collectors.toList()).stream().forEach(x -> System.out.println(x));
//方式二
List<Double> doubleCode = cost.stream().filter(x -> x > 3).collect(Collectors.toList());
doubleCode.stream().forEach(x -> System.out.println(x));
}
```
6. 与工具包java.util.function配合
除了在语言层面支持了函数式编程,而且增添了一个新包,包名叫java.util.function。该包包含了很多类,来支持Java的函数式编程,其中一个类是Predicate,使用java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。Predicate接口非常适用于做过滤。该包下的其他类放到姐妹篇里面讲解
函数式接口名 |
作用 |
Function<T, R> |
接受一个参数T,返回结果R |
Predicate |
接受一个参数T,返回boolean |
Supplier |
不接受任何参数T,返回结果T |
Consumer |
接受一个参数T,不返回结果 |
UnaryOperator |
继承自Function<T,T>,返回相同类型T的结果 |
BiFunction<T, U, R> |
接收两个参数T,U,返回结果R |
BinaryOperator |
继承自BiFunction<T,T,T>,返回相同类型T的结果 |
Runnable |
实际上不接受任何参数,也不返回结果 |
Comparable |
实际上是接受两个相同类型的T,返回int |
Callable |
不接受任何参数,返回结果V |
```java
// 代码示例
public class test {
public static void main(String[] args) {
test t = new test();
List<String> launges = Arrays.asList("java", "shell", "python");
t.functionTest.apply(launges).stream().forEach(x -> System.out.println(x));
boolean flg = t.predicateTest.test(launges);
System.out.println(flg);
}
private Function<List<String>, List<String>> functionTest = in -> {
return in.stream().filter(x -> x.startsWith("j")).collect(Collectors.toList());
};
private Predicate<List<String>> predicateTest = in -> {
return in.stream().filter(x -> x.startsWith("j")).count() > 0;
};
}
```
Lambda表达式的优劣
- 优势
- 代码简洁
- 易并行计算
- 方便了函数式编程
- 改善了集合的操作(引入了Stream API)
- 劣势
- 代码可读性变差
- Debug调试变得困难
- 不可以直接在foreach中修改外面的值
- 在很多非并行运算中,性能未必有传统的for性能要高
文章借鉴处
- https://www.cnblogs.com/ysySelf/p/10937725.html
- https://www.runoob.com/java/java8-lambda-expressions.html
- http://blog.oneapm.com/apm-tech/226.html
- https://objcoding.com/2019/03/04/lambda/