Java8之Lambda 表达式(箭头函数)

函数式编程思想

在数学中 函数就是有输入量和输出量的一套计算方案 也就是“拿什么东西做什么事情”
相对而言 面向对象过分强调必须通过对象的形式来做事情
而函数式思想则尽量忽略面向对象的复杂语法——强调做什么 而不是以什么形式做

  • 面向对象的思想:
    做一件事情 找一个能解决这个事情的对象 调用对象的方法 完成事情
  • 函数式编程思想:
    只要能获取到结果 谁去做的 怎么做的都不重要,重视的是结果 不重视过程

可能不太好理解 我们举个简单的栗子:

代码冗余

public static void main(String[] args) {
        RunnableImpl runnable=new RunnableImpl();
        Thread thread=new Thread(runnable);
        thread.start();

        // 使用匿名内部类简化
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " - new");
            }
        };
        new Thread(runnable1).start();

        // 继续简化
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " - new");
            }
        }).start();
    }

在这段多线程代码中:

  1. Thread 类需要 Runnable 接口作为参数 因为其中的抽象 run 方法是用来指定线程任务内容的核心
  2. 为了指定 run的方法体 不得不需要存在一个Runnable接口的实现类
  3. 为了省去定义一个RunnableImpl实现类的麻烦 不得不使用匿名内部类
  4. 因为必须覆盖重写抽象的run方法 所以方法名称 方法参数和方法返回值都不得不再写一遍 且不能写错
    而实际上 只有方法体才是关键所在
    因此 我们需要转换我们的编程思想
    更加关注做什么 而不是怎么做

我们为了做这件事情而不得不创建一个匿名内部类对象
而我们真正希望做的事情是将 run 方法体内的代码传递给 Thread 类

传递一段代码——这才是真正的目的

更加简便:

在2014年3月Oracle所发布的Java 8(JDK 1.8)中 加入了Lambda表达式的重量级新特性 打开了新世界的大门
如下案例:

public static void main(String[] args) {
    // 使用匿名内部类的方式实现多线程
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " - new");
        }
    }).start();

    //使用Lambda表达式实现多线程
    new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " - new");
        }
    ).start();
}

我们来详细看一下:
Runnable 接口只有一个 run 方法的定义:public abstract void run()
即制定了一种做事情的方案(其实就是一个函数):

无参数:不需要任何条件即可执行该方案
无返回值:该方案不产生任何结果
代码块(方法体):该方案的具体执行步骤
在Lambda表达式中更为简单:() ‐> System.out.println(“方法执行”)

前面的一对小括号即 run 方法的参数(无) 代表不需要任何条件
中间的一个箭头代表将前面的参数传递给后面的代码
后面的输出语句即业务逻辑代码


一、Lambda表达式的标准格式

Lambda省去了面向对象语法的条条框框
格式由3个部分组成:

  • 一些参数
  • 一个箭头
  • 一段代码
    标准格式:
(参数类型 参数名称) ‐> { 代码语句 }

小括号内的语法与传统方法参数列表一致:无参数则留空 多个参数则用逗号分隔
-> 是新引入的语法格式 代表指向动作
大括号内的语法与传统方法体要求基本一致

二、案例:

1. 无参数无返回值

接口类:

public interface Cook {
    public abstract void makeFood();
}

测试类:

public class CookTest {
    public static void main(String[] args) {
        // 调用invokeCook()方法 传递Cook接口的匿名内部类对象
        invokeCook(new Cook() {
            @Override
            public void makeFood() {
                System.out.println("开饭了");
            }
        });

        // 使用Lambda表达式简化匿名内部类的书写
        invokeCook(() -> {
            System.out.println("开饭了");
        });
    }

    // 定义一个方法 参数传递Cook接口 方法内部调用Cook接口中的makeFood()方法
    public static void invokeCook(Cook cook)
    {
        cook.makeFood();
    }
}
2. 有参数有返回值
  • 例一、
  • 实体类:
public class Person {
    private String name;
    private int age;

    ...
}

测试类:

public class ArraysTest {
    public static void main(String[] args) {
        // 使用数组存储多个Person对象
        Person[] arr={new Person("小A",10),
                	new Person("小B",13),
                	new Person("小C",12)};

        // 升序操作
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });

        // 使用Lambda表达式简化匿名内部类的书写
        Arrays.sort(arr,(Person o1, Person o2) -> {
            return o1.getAge()-o2.getAge();
        });

        // 遍历输出结果
        for (Person p:arr)
        {
            System.out.println(p);
        }
    }
}
  • 例二、计算类:
public interface Calculate {
    public abstract int calc(int a,int b);
}

测试类:

public class CalculateTest {
    public static void main(String[] args) {
        // 调用方法 参数是一个接口 可以使用匿名内部类
        invokeCalc(1, 2, new Calculate() {
            @Override
            public int calc(int a, int b) {
                return a+b;
            }
        });

        // 使用Lambda表达式简化匿名内部类的书写
        invokeCalc(1,2,(int a,int b) -> {
            return a+b;
        });
    }

    // 参数传递两个int类型的整数和Calculate接口 内部调用方法计算两个整数的和
    public static void invokeCalc(int a,int b,Calculate calculate)
    {
        System.out.println(calculate.calc(a, b));
    }
}

三、Lambda省略格式

Lambda表达式是可推导可省略的
凡是根据上下文推导出来的内容 都可以省略书写

可省略的内容:

  1. 参数列表:括号中参数列表的数据类型可以省略不写 因为在接口里已经定义了类型
  2. 参数列表:括号中的参数如果只有一个 那么类型和()都可以省略
  3. 代码:如果{}中的代码只有一行 那么无论是否有返回值 都可以省略{}和return和分号 但要省略必须一起省略
public static void main(String[] args) {
   // 在JDK1.7版本之前 必须把前后的泛型都写上
    ArrayList<String> arrayList1=new ArrayList<String>();

    // 在JDK1.7版本之后 等号后的泛型可省略 因为可以根据前面的泛型推导出来
    ArrayList<String> arrayList2=new ArrayList<>();
}

无参数无返回值:

// 调用invokeCook()方法 传递Cook接口的匿名内部类对象
invokeCook(new Cook() {
    @Override
    public void makeFood() {
        System.out.println("开饭了");
    }
});

// 使用Lambda表达式简化匿名内部类的书写
invokeCook(() -> {
    System.out.println("吃饭了");
});

// 优化省略Lambda ★
invokeCook(() -> System.out.println("吃饭了"));

有参数有返回值:

// 调用方法 参数是一个接口 可以使用匿名内部类
invokeCalc(1, 2, new Calculate() {
     @Override
     public int calc(int a, int b) {
         return a+b;
     }
 });

 // 使用Lambda表达式简化匿名内部类的书写
 invokeCalc(1,2,(int a,int b) -> {
     return a+b;
 });

 // 优化省略Lambda ★
 invokeCalc(1,2,(a,b) -> a+b);
总结:

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

注:有且仅有一个抽象方法的接口称为函数式接口

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值