一、Lambda表达式简介

什么是Lambda?

Lambda是JAVA 8添加的新特性,说白了,Lambda是一个匿名函数

为什么使用Lambda

使用Lambda表达式可以对一个接口的方法进行非常简洁的实现

Lambda对接口的要求

虽然可以使用Lambda表达式对某些接口进行简单的实现,但是并不是所有的接口都可以用Lambda表达式来实现,要求接口中定义的必须要实现的抽象方法只能是一个

  1. JAVA8 ,对接口加了一个新特性:default
  2. 可以使用default对接口方法进行修饰,被修饰的方法在接口中可以默认实现

_@_FunctionalInterface

修饰函数式接口的,接口中的抽象方法只有一个

二、Lambda的基础语法

2.1 语法

  1. // 1.Lambda表达式的基础语法
  2. // Lambda是一个匿名函数 一般关注的是以下两个重点
  3. // 参数列表 方法体
  4. /**
  5. * ():用来描述参数列表
  6. * {}:用来描述方法体 有时可以省略
  7. * ->: Lambda运算符 读作goes to
  8. * 例 Test t=()->{System.out.println("hello word")};
  9. */

2.2 创建多个接口

  1. /**
  2. * 无参数无返回值接口
  3. * @author Alan
  4. * @version 1.0
  5. * @date 2020-05-27 10:24
  6. */
  7. @FunctionalInterface
  8. public interface LambdaNoneReturnNoneParmeter {
  9. void test();
  10. }
  11. /**
  12. * 无返回值有单个参数
  13. * @author Alan
  14. * @version 1.0
  15. * @date 2020-05-27 10:26
  16. */
  17. @FunctionalInterface
  18. public interface LambdaNoneReturnSingleParmeter {
  19. void test(int n);
  20. }
  21. /**
  22. * 无返回值 多个参数的接口
  23. * @author Alan
  24. * @version 1.0
  25. * @date 2020-05-27 10:27
  26. */
  27. @FunctionalInterface
  28. public interface LambdaNoneReturnMutipleParmeter {
  29. void test(int a,int b);
  30. }
  31. /**
  32. * 有返回值 无参数接口
  33. * @author Alan
  34. * @version 1.0
  35. * @date 2020-05-27 10:28
  36. */
  37. @FunctionalInterface
  38. public interface LambdaSingleReturnNoneParmeter {
  39. int test();
  40. }
  41. /**
  42. * 有返回值 有单个参数的接口
  43. * @author Alan
  44. * @version 1.0
  45. * @date 2020-05-27 10:29
  46. */
  47. @FunctionalInterface
  48. public interface LambdaSingleReturnSingleParmeter {
  49. int test(int n);
  50. }
  51. /**
  52. * 有返回值 有多个参数的接口
  53. * @author Alan
  54. * @version 1.0
  55. * @date 2020-05-27 10:30
  56. */
  57. @FunctionalInterface
  58. public interface LambdaSingleReturnMutipleParmeter {
  59. int test(int a,int b);
  60. }

2.3 创建测试类

  1. package com.alan.learn.syntax;
  2. import com.alan.learn.interfaces.*;
  3. public class Syntax1 {
  4. public static void main(String[] args) {
  5. // 1.Lambda表达式的基础语法
  6. // Lambda是一个匿名函数 一般关注的是以下两个重点
  7. // 参数列表 方法体
  8. /**
  9. * ():用来描述参数列表
  10. * {}:用来描述方法体
  11. * ->: Lambda运算符 读作goes to
  12. */
  13. // 无参无返回
  14. LambdaNoneReturnNoneParmeter lambda1=()->{
  15. System.out.println("hello word");
  16. };
  17. lambda1.test();
  18. // 无返回值 单个参数
  19. LambdaNoneReturnSingleParmeter lambda2=(int n)->{
  20. System.out.println("参数是:"+n);
  21. };
  22. lambda2.test(10);
  23. // 无返回值 多个参数
  24. LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{
  25. System.out.println("参数和是:"+(a+b));
  26. };
  27. lambda3.test(10,12);
  28. // 有返回值 无参数
  29. LambdaSingleReturnNoneParmeter lambda4=()->{
  30. System.out.println("lambda4:");
  31. return 100;
  32. };
  33. int ret=lambda4.test();
  34. System.out.println("返回值是:"+ret);
  35. // 有返回值 单个参数
  36. LambdaSingleReturnSingleParmeter lambda5=(int a)->{
  37. return a*2;
  38. };
  39. int ret2= lambda5.test(3);
  40. System.out.println("单个参数,lambda5返回值是:"+ret2);
  41. //有返回值 多个参数
  42. LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{
  43. return a+b;
  44. };
  45. int ret3=lambda6.test(12,14);
  46. System.out.println("多个参数,lambda6返回值是:"+ret3);
  47. }
  48. }
  49. ------------------------输出结果:----------------------------------
  50. hello word
  51. 参数是:10
  52. 参数和是:22
  53. lambda4
  54. 返回值是:100
  55. 单个参数,lambda5返回值是:6
  56. 多个参数,lambda6返回值是:26

三、语法精简

针对上述基础语法的精简

3.1 参数类型精简

  1. /**
  2. * 语法精简
  3. * 1.参数类型
  4. * 由于在接口的抽象方法中,已经定义了参数的数量类型 所以在Lambda表达式中参数的类型可以省略
  5. * 备注:如果需要省略类型,则每一个参数的类型都要省略,千万不要一个省略一个不省略
  6. */
  7. LambdaNoneReturnMutipleParmeter lambda1=(int a,int b)-> {
  8. System.out.println("hello world");
  9. };
  10. //可以精简为:
  11. LambdaNoneReturnMutipleParmeter lambda1=(a,b)-> {
  12. System.out.println("hello world");
  13. };

3.2 参数小括号精简

  1. /**
  2. * 2.参数小括号
  3. * 如果参数列表中,参数的数量只有一个 此时小括号可以省略
  4. */
  5. LambdaNoneReturnSingleParmeter lambda2=(a)->{
  6. System.out.println("hello world");
  7. };
  8. //可以精简为:
  9. LambdaNoneReturnSingleParmeter lambda2= a->{
  10. System.out.println("hello world");
  11. };

3.3 方法大括号精简

  1. /**
  2. * 3.方法大括号
  3. * 如果方法体中只有一条语句,此时大括号可以省略
  4. */
  5. LambdaNoneReturnSingleParmeter lambda3=a->{
  6. System.out.println("hello world");
  7. };
  8. //可以精简为:
  9. LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");

3.4 大括号精简补充

  1. /**
  2. * 4.如果方法体中唯一的一条语句是一个返回语句
  3. * 贼省略大括号的同时 也必须省略return
  4. */
  5. LambdaSingleReturnNoneParmeter lambda4=()->{
  6. return 10;
  7. };
  8. //可以精简为:
  9. LambdaSingleReturnNoneParmeter lambda4=()->10;

3.5 多参数,有返回值 精简

  1. LambdaSingleReturnNoneParmeter lambda4=(a,b)->{
  2. return a+b;
  3. };
  4. //可以精简为:
  5. LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;

四、Lambda语法进阶

4.1 方法引用(普通方法与静态方法)

在实际应用过程中,一个接口在很多地方都会调用同一个实现,例如:

  1. LambdaSingleReturnMutipleParmeter lambda1=(a,b)->a+b;
  2. LambdaSingleReturnMutipleParmeter lambda2=(a,b)->a+b;

这样一来每次都要写上具体的实现方法 a+b,如果需求变更,则每一处实现都需要更改,基于这种情况,可以将后续的是实现更改为已定义的 方法,需要时直接调用就行

语法:

  1. /**
  2. *方法引用:
  3. * 可以快速的将一个Lambda表达式的实现指向一个已经实现的方法
  4. * 方法的隶属者 如果是静态方法 隶属的就是一个类 其他的话就是隶属对象
  5. * 语法:方法的隶属者::方法名
  6. * 注意:
  7. * 1.引用的方法中,参数数量和类型一定要和接口中定义的方法一致
  8. * 2.返回值的类型也一定要和接口中的方法一致
  9. */

例:

  1. package com.alan.learn.syntax;
  2. import com.alan.learn.interfaces.LambdaSingleReturnSingleParmeter;
  3. public class Syntax3 {
  4. public static void main(String[] args) {
  5. LambdaSingleReturnSingleParmeter lambda1=a->a*2;
  6. LambdaSingleReturnSingleParmeter lambda2=a->a*2;
  7. LambdaSingleReturnSingleParmeter lambda3=a->a*2;
  8. //简化
  9. LambdaSingleReturnSingleParmeter lambda4=a->change(a);
  10. //方法引用
  11. LambdaSingleReturnSingleParmeter lambda5=Syntax3::change;
  12. }
  13. /**
  14. * 自定义的实现方法
  15. */
  16. private static int change(int a){
  17. return a*2;
  18. }
  19. }

4.2 方法引用(构造方法)

目前有一个实体类

  1. public class Person {
  2. public String name;
  3. public int age;
  4. public Person() {
  5. System.out.println("Person的无参构造方法执行");
  6. }
  7. public Person(String name, int age) {
  8. this.name = name;
  9. this.age = age;
  10. System.out.println("Person的有参构造方法执行");
  11. }
  12. }

需求:两个接口,各有一个方法,一个接口的方法需要引用Person的无参构造,一个接口的方法需要引用Person的有参构造 用于返回两个Person对象,例:

  1. interface PersonCreater{
  2. //通过Person的无参构造实现
  3. Person getPerson();
  4. }
  5. interface PersonCreater2{
  6. //通过Person的有参构造实现
  7. Person getPerson(String name,int age);
  8. }

那么可以写作:

  1. public class Syntax4 {
  2. public static void main(String[] args) {
  3. PersonCreater creater=()->new Person();
  4. //引用的是Person的无参构造
  5. //PersonCreater接口的方法指向的是Person的方法
  6. PersonCreater creater1=Person::new; //等价于上面的()->new Person()
  7. //实际调用的是Person的无参构造 相当于把接口里的getPerson()重写成new Person()。
  8. Person a=creater1.getPerson();
  9. //引用的是Person的有参构造
  10. PersonCreater2 creater2=Person::new;
  11. Person b=creater2.getPerson("张三",18);
  12. }
  13. }

注意:是引用无参构造还是引用有参构造 在于接口定义的方法参数

五、综合练习案例

5.1 集合排序案例

  1. package com.alan.exercise;
  2. import com.alan.learn.data.Person;
  3. import java.util.ArrayList;
  4. public class Exercise1 {
  5. public static void main(String[] args) {
  6. //需求:已知在一个ArrayList中有若干各Person对象,将这些Person对象按照年龄进行降序排列
  7. ArrayList<Person> list=new ArrayList<>();
  8. list.add(new Person("张三",10));
  9. list.add(new Person("李四",12));
  10. list.add(new Person("王五",13));
  11. list.add(new Person("赵六",14));
  12. list.add(new Person("李雷",11));
  13. list.add(new Person("韩梅梅",8));
  14. list.add(new Person("jack",10));
  15. System.out.println("排序前:"+list);
  16. //将排列的依据传入 具体的方法指向的是 内部元素的age相减 sort会依据结果的正负进行降序排列
  17. //sort 使用提供的 Comparator对此列表进行排序以比较元素。
  18. list.sort((o1, o2) -> o2.age-o1.age);
  19. System.out.println("排序后:"+list);
  20. }
  21. }

5.2 Treeset排序案例

  1. package com.alan.exercise;
  2. import com.alan.learn.data.Person;
  3. import java.util.TreeSet;
  4. public class Exercise2 {
  5. public static void main(String[] args) {
  6. /**Treeset 自带排序
  7. * 但是现在不知道Person谁大谁小无法排序
  8. * 解决方法:
  9. * 使用Lambda表达式实现Comparator接口,并实例化一个TreeSet对象
  10. * 注意:在TreeSet中如果Comparator返回值是 0 会判断这是两个元素是相同的 会进行去重
  11. * TreeSet<Person> set=new TreeSet<>((o1, o2) -> o2.age-o1.age);
  12. * 这个获取的对象打印会少一个Person
  13. * 此时我们将方法修改
  14. */
  15. TreeSet<Person> set=new TreeSet<>((o1, o2) ->{
  16. if(o1.age>=o2.age){
  17. return -1;
  18. }else {
  19. return 1;
  20. }
  21. });
  22. set.add(new Person("张三",10));
  23. set.add(new Person("李四",12));
  24. set.add(new Person("王五",13));
  25. set.add(new Person("赵六",14));
  26. set.add(new Person("李雷",11));
  27. set.add(new Person("韩梅梅",8));
  28. set.add(new Person("jack",10));
  29. System.out.println(set);
  30. }
  31. }

5.3 集合的遍历

  1. package com.alan.exercise;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. public class Exercise3 {
  5. public static void main(String[] args) {
  6. ArrayList<Integer> list=new ArrayList<>();
  7. Collections.addAll(list,1,2,3,4,5,6,7,8,9);
  8. /**
  9. * list.forEach(Consumer<? super E> action)
  10. * api文档解释: 对 集合中的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
  11. * 将集合中的每一个元素都带入到接口Consumer的方法accept中 然后方法accept指向我们的引用
  12. * 输出集合中的所有元素
  13. * list.forEach(System.out::println);
  14. */
  15. //输出集合中所有的偶数
  16. list.forEach(ele->{
  17. if(ele%2==0){
  18. System.out.println(ele);
  19. }
  20. });
  21. }
  22. }

5.4 删除集合中满足条件的元素

  1. package com.alan.exercise;
  2. import com.alan.learn.data.Person;
  3. import java.util.ArrayList;
  4. public class Exercise4 {
  5. public static void main(String[] args) {
  6. ArrayList<Person> list=new ArrayList<>();
  7. list.add(new Person("张三",10));
  8. list.add(new Person("李四",12));
  9. list.add(new Person("王五",13));
  10. list.add(new Person("赵六",14));
  11. list.add(new Person("李雷",11));
  12. list.add(new Person("韩梅梅",8));
  13. list.add(new Person("jack",10));
  14. //删除集合中年龄大于12的元素
  15. /**
  16. * 之前迭代器的做法
  17. * ListIterator<Person> it = list.listIterator();
  18. * while (it.hasNext()){
  19. * Person ele=it.next();
  20. * if(ele.age>12){
  21. * it.remove();
  22. * }
  23. * }
  24. */
  25. /**
  26. * lambda实现
  27. * 逻辑
  28. * 将集合中的每一个元素都带入到接口Predicate的test方法中,
  29. * 如果返回值是true,则删除这个元素
  30. */
  31. list.removeIf(ele->ele.age>10);
  32. System.out.println(list);
  33. }
  34. }

5.5 开辟一条线程 做一个数字的输出

  1. package com.alan.exercise;
  2. public class Exercise5 {
  3. public static void main(String[] args) {
  4. /**
  5. * 通过Runnable 来实例化线程
  6. */
  7. Thread t=new Thread(()->{
  8. for(int i=0;i<100;i++){
  9. System.out.println(i);
  10. }
  11. });
  12. t.start();
  13. }
  14. }

六、系统内置的函数式接口

  1. package com.alan.functional;
  2. import java.util.function.*;
  3. /**
  4. * 系统内置的一些函数式接口
  5. */
  6. public class FunctionalInterface {
  7. public static void main(String[] args) {
  8. // Predicate<T> : 参数是T 返回值boolean
  9. // 在后续如果一个接口需要指定类型的参数,返回boolean时可以指向 Predicate
  10. // IntPredicate int -> boolean
  11. // LongPredicate long -> boolean
  12. // DoublePredicate double -> boolean
  13. // Consumer<T> : 参数是T 无返回值(void)
  14. // IntConsumer int ->void
  15. // LongConsumer long ->void
  16. // DoubleConsumer double ->void
  17. // Function<T,R> : 参数类型T 返回值R
  18. // IntFunction<R> int -> R
  19. // LongFunction<R> long -> R
  20. // DoubleFunction<R> double -> R
  21. // IntToLongFunction int -> long
  22. // IntToDoubleFunction int -> double
  23. // LongToIntFunction long -> int
  24. // LongToDoubleFunction long -> double
  25. // DoubleToLongFunction double -> long
  26. // DoubleToIntFunction double -> int
  27. // Supplier<T> : 参数 无 返回值T
  28. // UnaryOperator<T> :参数T 返回值 T
  29. // BiFunction<T,U,R> : 参数 T、U 返回值 R
  30. // BinaryOperator<T> :参数 T、T 返回值 T
  31. // BiPredicate<T,U> : 参数T、U 返回值 boolean
  32. // BiConsumer<T,U> : 参数T、U 无返回值
  33. /**
  34. * 常用的 函数式接口
  35. * Predicate<T>、Consumer<T>、Function<T,R>、Supplier<T>
  36. */
  37. }
  38. }

七、Lambda闭包

  1. package com.alan.closure;
  2. import java.util.function.Supplier;
  3. public class ClosureDemo {
  4. public static void main(String[] args) {
  5. /**
  6. * lambda的闭包会提升包围变量的生命周期
  7. * 所以局部变量 num在getNumber()方法内被 get()引用 不会在getNumber()方法执行后销毁
  8. * 这种方法可以在外部获取到某一个方法的局部变量
  9. */
  10. int n=getNumber().get();
  11. System.out.println(n);
  12. }
  13. private static Supplier<Integer> getNumber(){
  14. int num=10;
  15. /**
  16. * Supplier supplier=()->num;
  17. * return supplier;
  18. */
  19. return ()->{
  20. return num;
  21. };
  22. }
  23. }
  24. *************************************************************************
  25. package com.alan.closure;
  26. import java.util.function.Consumer;
  27. public class ClosureDemo2 {
  28. public static void main(String[] args) {
  29. int a=10;
  30. Consumer<Integer> c=ele->{
  31. System.out.println(a+1);
  32. //System.out.println(ele);
  33. //System.out.println(a++); 会报错
  34. //在lambda中引用局部变量 这个变量必须是一个常量
  35. };
  36. //a++; 这样也会导致内部报错
  37. //如果在内部已经引用局部变量 参数传递后 打印的还是 10
  38. c.accept(1);
  39. }
  40. }