• Java基础之lambda表达式(JDK1.8新特性)


    Lambda表达式

    Lambda表达式允许把函数作为一个方法的参数(函数作为参数传递进方法中)。函数式接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为lambda表达式。

    各种函数式接口

    java.lang.Runnable
    java.util.concurrent.Callable
    java.security.PrivilegedAction
    java.util.Comparator
    java.io.FileFilter
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JDK1.8 新增加的函数接口:

    java.util.function
    
    • 1

    java.util.function 包下包含了很多类,用来支持Java的函数式编程

    接口&描述
    BiConsumer代表了一个接受两个输入参数的操作,并且不返回任何结果
    BiFunction代表了一个接受两个输入参数的方法,并且返回一个结果
    BinaryOperaror代表了一个作用于两个同类型操作符的操作,并且返回了操作符同类型的结果
    BiPredicate代表了一个两个参数的boolean值方法
    BooleanSupplier代表了boolean值结果的提供方
    Consumer代表了接受一个输入参数并且无返回的操作
    DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果
    DoubleConsumer代表了一个接受double值参数的操作,并且不返回结果
    DoubleFunction代表接受一个double值参数的方法,并且有返回值
    Comparator这个接口最主要的作用就是比较,其核心的方法是 compare(T o1, T o2),当 o1比o2小返回-1,当o1等于o2返回0,当o1大于o2返回1

    Lambda的语法

    (parameters) -> expression
    或
    (parameters) ->{ statements; }
    
    • 1
    • 2
    • 3
    1. 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
    2. 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
    3. 可选的大括号:如果主体包括了一个语句,就不需要使用大括号
    4. 可选的返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值,大括号需要指明表达式返回的一个数值。

    Lambda 表达实例

            //1. 不需要参数,返回值为5
            ()->5;
            //2. 接收一个参数(数字类型),返回其2倍的值
            x ->2*x
            //3.接受2个参数(数字),并返回他们的差值
            (x,y)->x - y;
            //4.接收2个int型整数,返回他们的和int x,int y)->x+y;
            //5. 接收一个String对象,并在控制台打印,不返回任何值String s)->System.out.print(s);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    举例说明

    public class Java8Tester {
        public static void main(String[] args) {
       Java8Tester tester = new Java8Tester();
    
            //类型声明
            MathOperation addition = (int a, int b) -> a + b;
            //不用声明类型
            MathOperation subtraction = (a, b) -> a - b;
            //大括号中的返回语句
            MathOperation multipliaction = (int a, int b) -> {
                return a * b;
            };
            //没有大括号及返回语句
            MathOperation division = (int a, int b) -> a / b;
    
            System.out.println("10+5=" + tester.operate(10, 5, addition));
            System.out.println("10-5=" + tester.operate(10, 5, subtraction));
            System.out.println("10*5=" + tester.operate(10, 5, multipliaction));
            System.out.println("10/5=" + tester.operate(10, 5, division));
    
            //不用括号
            GreetingService greetingService1 = message -> System.out.println("Hello " + message);
    
            //用括号
            GreetingService greetingService2 = (message) -> System.out.println("Hello " + message);
    
            greetingService1.sayMessage("Runoob");
            greetingService2.sayMessage("Google");
    	 }
        interface MathOperation{
            int operation(int a, int b);
        }
        interface GreetingService{
            void sayMessage(String message);
        }
        private int operate(int a, int b, MathOperation mathOperation) {
            return mathOperation.operation(a, b);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    变量作用域

    lambda 表达式只能引用标记了final的外层局部变量,也就是说不能再lambda内部修改定义在域外的局部变量,否则会编译报错。 在lambda表达式中,只能引用值不会改变的变量。 这是因为如果在lambda表达式中改变变量,并发执行多个动作时就会不安全。对于目前为止我们看到的动作不会发生这种情况。

    public class Java8Test2 {
        final static String salutation = "Hello!";
    
        public static void main(String[] args) {
            GreetingService greetingService = message -> System.out.println(salutation + message);
            greetingService.sayMessage("Runoob");
        }
    
        interface GreetingService {
            void sayMessage(String message);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    处理lambda 表达式

    使用lambda表达式的重点是延迟执行。毕竟,如果想要立即执行代码,完全可以直接执行,而无需把它包装在一个
    lambda表达式中。之所以希望以后执行代码,这有很多原因,如:

    • 在一个单独的线程中运行代码:
    • 多次运行代码;
    • 在算法的适当位置运行代码(例如:排序中的比较操作);
    • 发生某种情况时执行代码(如,点击了一个按钮,数据到达,等等);
    • 只在必要时才运行代码。

    例如:假设你想要执行一个动作n次,将这个动作和重复次数传递到一个 repeat 方法:
    repeat(10,()->System.out.println("Hello,world"))
    要接受这个lambda表达式,需要选择一个函数式接口。例如,我们可以使用 Runnable 接口:

     public static void repeat(int n,Runnable action){
          for(int i=0;i<n;i++){
            action.run();
          }
     }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果要告诉动作出现在某次迭代中。

    /**
         * 在某次迭代中执行动作
         * @param n
         * @param action
         */
        public static void repeat(int n, IntConsumer action) {
            for (int i = 0; i < n; i++) {
                action.accept(i);
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    变量作用域

    lambda 表达式有3个部分

    1. 一个代码块;
    2. 参数;
    3. 自由变量的值,这里指非参数而且不在代码中定义的变量。

    函数式接口

    函数接口,是指内部只有一个抽象方法的接口。但是可以有多个非抽象方法的接口,函数式接口可以被隐式转换为lambda表达式。

    使用实例1
    public class PredicateTest {
        public static void main(String[] args) {
            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
            /**
             *
             * Predicate dispatcher = n -> true;
             n 是一个参数传递到Predicate接口的test方法
             n 如果存在test方法返回true
             */
            System.out.println("输出所有数据:");
            //传递参数n
            eval(list, n -> true);
    
    //        Predicate dispatcher = n -> true;
    //        n 是一个参数传递到Predicate接口的test方法
    //        如果n%2为0,test方法返回true
            System.out.println("******输出所有的偶数:");
            eval(list, n -> n % 2 == 0);
    
    //        Predicate predicate2=n->n>3
    //        n是一个参数传递到Predicate接口的test方法
    //        如果n大于3 test方法返回true
            System.out.println("输出大于3的所有数字:");
            eval(list, n -> n > 3);
        }
    
        public static void eval(List<Integer> list, Predicate<Integer> predicate) {
            for (Integer n : list) {
                if (predicate.test(n)) {
                    System.out.println(n+" ");
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    使用实例2
    public class CompareTest {
    
        public static void main(String[] args) {
            List<String> words = Arrays.asList("apple", "banana", "pear");
    
            compareLength("apple", "banana", (n1, n2) -> -1);
    
            compareLength("banana", "pear", (n1, n2) -> 1);
    
            compareLength("banana", "banana", (n1, n2) -> 0);
    
    
            words.sort(Comparator.comparingInt(String::length));
        }
    
        public static void compareLength(String n1, String n2, Comparator comparator) {
            if (comparator.compare(n1, n2) == 0) {
                System.out.println("n1==n2" + n1 + "  " + n2);
            }
            if (comparator.compare(n1, n2) > 0) {
                System.out.println("n1>n2" + n1 + "  " + n2);
            }
            if (comparator.compare(n1, n2) < 0) {
                System.out.println("n1 + n1 + "  " + n2);
            }
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    使用示例3(集合排序)

    现在我们有一个集合list,集合的里的数据类型是HashMap。那么我们如何根据HashMap中的某个key给list排序呢?

    public class ListSortTest {
        public static void main(String[] args) {
            List<Map<String, Object>> list = new ArrayList<>();
            Map<String, Object> map1 = new HashMap<>();
            map1.put("id", 2);
            map1.put("name", "张二");
            list.add(map1);
            Map<String, Object> map2 = new HashMap<>();
            map2.put("id", 1);
            map2.put("name", "张一");
            list.add(map2);
            Map<String, Object> map3 = new HashMap<>();
            map3.put("id", 3);
            map3.put("name", "张三");
            list.add(map3);
            //升序排列
            Collections.sort(list, (o1, o2) -> {
                int o1Id = (int) o1.get("id");
                int o2Id = (int) o2.get("id");
                if (o1Id > o2Id) {
                    return 1;
                } else {
                    return -1;
                }
            });
            //降序排列
            Collections.sort(list, new Comparator<Map<String, Object>>() {
                @Override
                public int compare(Map<String, Object> o1, Map<String, Object> o2) {
                    int o1Id = (int) o1.get("id");
                    int o2Id = (int) o2.get("id");
                    if (o1Id > o2Id) {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            });
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    使用示例4(按照对象属性给list排序)
    public class ListSortTest2 {
        public static void main(String[] args) {
            Student student1 = new Student(1, "张三");
            Student student2 = new Student(2, "李四");
            List<Student> studentList = new ArrayList<>();
            studentList.add(student1);
            studentList.add(student2);
    
            //升序
            studentList.sort((o1, o2) -> {
                int o1Id = o1.getId();
                int o2Id = o2.getId();
                if (o1Id > o2Id) {
                    return 1;
                } else {
                    return -1;
                }
            });
    
            //降序
            studentList.sort((o1, o2) -> {
                int o1Id = o1.getId();
                int o2Id = o2.getId();
                if (o1Id > o2Id) {
                    return -1;
                } else {
                    return 1;
                }
            });
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    使用示例4

    自定义一个函数式接口

    public class SimpleLambda {
        public static void main(String[] args) {
            start(()-> System.out.println("调用函数式接口"));
        }
    
        public static void start(MyRunnable runnable) {
            new Thread(runnable).start();
        }
    
        //1. 只能有一个抽象方法
        //2.默认方法除外
        //3.可以加FunctionalInterface注解,也可以不加
        public interface MyRunnable extends Runnable {
            default void myRun() {
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 自定义一个函数式接口,传入参数,多种不同的代码编写特性:

      public class SimpleLambda2 {
          public static void main(String[] args) {
              // 1.单行表达式,可以省略return
              run(name -> String.format(name));
              // 2.代码块
              run(name -> {
                  String name1 = name;
                  return "序列化" + name1;
              });
              //3.方法引用,静态方法引用
              run(SimpleLambda2::toFormat);
              //4.普通方法引用
              run(new SimpleLambda2()::toFormat2);
      
          }
      
          static void run(Format format) {
              format.format("飞哥");
          }
      
          static String toFormat(String param) {
              return "序列化" + param;
          }
      
          String toFormat2(String param) {
              return "序列化" + param;
          }
      
          public interface Format {
              String format(String name);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
    2. 自定义一个函数式接口,带泛型的方法型函数

      public class SimpleLambda4 {
          public static void main(String[] args) {
              Apple apple = new Apple(1, "红色", 12, "安徽");
              Banner banner1 = doBuild(apple, apple1 -> {
                  Banner banner = new Banner();
                  banner.setPrice(apple1.getPrice());
                  banner.setColor(apple1.getColor());
                  return banner;
              });
              System.out.println(banner1);
      
          }
      
          public static Banner doBuild(Apple apple, Format format) {
              return format.build(apple);
          }
          @FunctionalInterface
          public interface Format<T extends Apple, R extends Banner> {
              R build(T t);
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

    总结

    本文详细介绍了lambda表达式,lambda表达式是JDK1.8最重要的特性。基本上所有的内部类都可以用lambda表达式来表示。灵活的运用lambda表达式和函数式接口可以大大的简化的程序开发。

    参考

    Java 8 Lambda 表达式
    [Java 8 函数式接口](

  • 相关阅读:
    MySQL中的日志(redo log、undo log、binlog)
    面试题 2023/11/15
    Android studio下载安装及配置-MacOS
    Spring框架笔记
    Leetcode.664 奇怪的打印机
    6-2 装载问题(分支限界)
    金仓数据库KingbaseES客户端编程接口指南-JDBC(8. JDBC 元数据处理)
    【云原生】k8s 管理平台 rancher
    selenium,元素操作以及浏览器操作方法
    《MySQL必知必会》知识汇总二
  • 原文地址:https://blog.csdn.net/u014534808/article/details/131970068