函数式编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

Lambda表达式的格式

Lambda 表达式基于数学中的 λ 演算得名,直接对应于其中的 lambda 抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

格式:

  1. (形式参数列表) -> {代码块}

组成Lambda表达式的三要素:参数列表,箭头,代码块

要素 说明
形式参数列表 如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可,实际上就是接口里面抽象方法的参数
-> 由英文中画线和大于符号组成,固定写法。代表指向动作
代码块 是我们具体要做的事情,也就是以前我们写的方法体内容

eg

  1. public class Demo04ThreadNameless {
  2. public static void main(String[] args) {
  3. new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. System.out.println("多线程任务执行!");
  7. }
  8. }).start();
  9. }
  10. }

上述 Runnable 接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效

  1. public class Demo04ThreadNameless {
  2. public static void main(String[] args) {
  3. new Thread(()->{
  4. System.out.println("多线程任务执行!");
  5. }).start();
  6. }
  7. }

Lambda 表达式对应的其实是一个 只有一个抽象方法 的接口(在 Java 中这种接口叫做功能接口)的匿名类。

无参数无返回值

  1. public class LambdaRunnable {
  2. public static void main(String[] args) {
  3. Runnable r = new Runnable() {
  4. @Override
  5. public void run() { // 方法无形参列表,也无返回值
  6. System.out.println("Hello, World");
  7. }
  8. };
  9. //匿名内部类
  10. r.run();
  11. // 通过 lambda 表达式来实例化 Runnable 接口的实现类
  12. Runnable l = () -> System.out.println("Hello, Lambda");
  13. l.run();
  14. }
  15. }

单参数无返回值

  1. public class ConsumerLambda {
  2. public static void main(String[] args) {
  3. // 单参数无返回值
  4. Consumer<String> c = new Consumer<String>() {
  5. @Override
  6. public void accept(String s) {
  7. System.out.println(s);
  8. }
  9. };
  10. c.accept("Hello World!");
  11. Consumer<String> l = (String s) -> {
  12. System.out.println(s);
  13. };
  14. l.accept("你好,世界!");
  15. }
  16. }

有参有返回值

操作步骤
给定一个计算器 Calculator 接口,内含抽象方法 calc 可以将两个int数字相加得到和值:

  1. public interface Calculator {
  2. int calc(int a,int b);
  3. }

示例代码

  1. public class Demo08InvokeCalc {
  2. private static void invokeCalc(int a, int b, Calculator calculator) {
  3. int result = calculator.calc(a, b);
  4. System.out.println("结果是:" + result);
  5. }
  6. public static void main(String[] args) {
  7. invokeCalc(1,2,(int a,int b)->{
  8. return a+b;
  9. });
  10. }
  11. }

省略的规则

在Lambda标准格式的基础上,使用省略写法的规则为:

省略数据类型

小括号内参数的类型可以省略;

  1. public static void main(String[] args) {
  2. invokeCalc(1, 2, (a, b) -> {
  3. return a + b;
  4. });
  5. }

省略参数的小括号

如果小括号内有且仅有一个参,则小括号可以省略;

  1. Consumer<String> l = (String s) -> {
  2. System.out.println(s);
  3. };
  4. l.accept("Hello World");
  5. Consumer<String> la = s -> {
  6. System.out.println(s);
  7. };
  8. la.accept("Hello Lambda");

省略 return 和大括号

如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

  1. public static void main(String[] args) {
  2. invokeCalc(1,2,(a,b)-> a+b);
  3. }

比较判断

  1. public class LambdaReturn {
  2. public static void main(String[] args) {
  3. // 省略 return 和 {} 前代码
  4. Comparator<Integer> comparator1 = (o1, o2) -> {
  5. return o1.compareTo(o2);
  6. };
  7. System.out.println(comparator1.compare(1, 2));
  8. // 省略 return 和 {} 后代码
  9. Comparator<Integer> comparator2 = (o1, o2) -> o1.compareTo(o2);
  10. System.out.println(comparator2.compare(3, 2));
  11. }
  12. }

注意事项

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
    无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断
    也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

Lambda表达式和匿名内部类的区别

所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成