函数式编程思想
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
Lambda表达式的格式
Lambda 表达式基于数学中的 λ 演算得名,直接对应于其中的 lambda 抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
格式:
(形式参数列表) -> {代码块}
组成Lambda表达式的三要素:参数列表,箭头,代码块
要素 | 说明 |
---|---|
形式参数列表 | 如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可,实际上就是接口里面抽象方法的参数 |
-> | 由英文中画线和大于符号组成,固定写法。代表指向动作 |
代码块 | 是我们具体要做的事情,也就是以前我们写的方法体内容 |
eg
public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start();
}
}
上述 Runnable 接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效
public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(()->{
System.out.println("多线程任务执行!");
}).start();
}
}
Lambda 表达式对应的其实是一个 只有一个抽象方法 的接口(在 Java 中这种接口叫做功能接口)的匿名类。
无参数无返回值
public class LambdaRunnable {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() { // 方法无形参列表,也无返回值
System.out.println("Hello, World");
}
};
//匿名内部类
r.run();
// 通过 lambda 表达式来实例化 Runnable 接口的实现类
Runnable l = () -> System.out.println("Hello, Lambda");
l.run();
}
}
单参数无返回值
public class ConsumerLambda {
public static void main(String[] args) {
// 单参数无返回值
Consumer<String> c = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
c.accept("Hello World!");
Consumer<String> l = (String s) -> {
System.out.println(s);
};
l.accept("你好,世界!");
}
}
有参有返回值
操作步骤
给定一个计算器 Calculator 接口,内含抽象方法 calc 可以将两个int数字相加得到和值:
public interface Calculator {
int calc(int a,int b);
}
示例代码
public class Demo08InvokeCalc {
private static void invokeCalc(int a, int b, Calculator calculator) {
int result = calculator.calc(a, b);
System.out.println("结果是:" + result);
}
public static void main(String[] args) {
invokeCalc(1,2,(int a,int b)->{
return a+b;
});
}
}
省略的规则
省略数据类型
小括号内参数的类型可以省略;
public static void main(String[] args) {
invokeCalc(1, 2, (a, b) -> {
return a + b;
});
}
省略参数的小括号
如果小括号内有且仅有一个参,则小括号可以省略;
Consumer<String> l = (String s) -> {
System.out.println(s);
};
l.accept("Hello World");
Consumer<String> la = s -> {
System.out.println(s);
};
la.accept("Hello Lambda");
省略 return 和大括号
如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
public static void main(String[] args) {
invokeCalc(1,2,(a,b)-> a+b);
}
比较判断
public class LambdaReturn {
public static void main(String[] args) {
// 省略 return 和 {} 前代码
Comparator<Integer> comparator1 = (o1, o2) -> {
return o1.compareTo(o2);
};
System.out.println(comparator1.compare(1, 2));
// 省略 return 和 {} 后代码
Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(3, 2));
}
}
注意事项
Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。 - 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
Lambda表达式和匿名内部类的区别
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成