zoukankan      html  css  js  c++  java
  • Lambda表达式(一)入门认识篇

    Lambda表达式(一)入门认识篇

    Lambda简介

    • Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构

    • JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

    • Lambda表达式是Java SE 8中一个重要的新特性。Lambda表达式允许你通过表达式来代替功能接口。Lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。

    • Lambda表达式还增强了集合库。 Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包。 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。 总的来说Lambda表达式和 stream 是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。

    Lambda定义

    Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:
    它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

    匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多

    函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。

    传递——Lambda表达式可以作为参数传递给方法或存储在变量中。

    简洁——无需像匿名类那样写很多模板代码。

    对接口的要求

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

    jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。

    函数式接口

    什么是函数式接口

    (1)只包含一个抽象方法的接口,称为函数式接口。

    (2)你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方 法上进行声明)。

    (3)我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包 含一条声明,说明这个接口是一个函数式接口。

    @FunctionalInterface

    修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。

    Lambda表达式语法

    基本语法

    • Java8中引入了一个新的操作符,"->",该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分;

      左侧: Lambda表达式的参数列表,对应的是接口中抽象方法的参数列表;
      右侧: Lambda表达式中所需要执行的功能(Lambda体),对应的是对抽象方法的实现;(函数式接口(只能有一个抽象方法))

    • Lambda表达式的实质是 对接口的实现;
    	(parameters) ->{ statements; }
    	//示例代码
    	Comparator<Apple> byWeight2  = (Apple o1, Apple o2)-> o1.getWeight().compareTo(o2.getWeight());
    

    解析:

    • 参数列表——这里它采用了 Comparatorcompare 方法的参数,两个 Apple
    • 箭头——箭头 -> 把参数列表与Lambda主体分隔开
    • Lambda主体——比较两个 Apple 的重量。表达式就是Lambda的返回值了

    Lambda语法示例

    下面是Java 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 对象,并在控制台打印,不返回任何值(看起来像是返回void)
    (String s) -> System.out.print(s)
    

    Lambda五大语法格式

    语法格式一:接口中的抽象方法 : 无参数,无返回值;

     @Test
        public void test1(){
            /**
             *语法格式一、
             *  接口中的抽象方法 : 无参数,无返回值;
             */
    
            /*final */int num = 2; //jdk1.7之前必须定义为final的下面的匿名内部类中才能访问
    
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello world!" + num); //本质还是不能对num操作(只是jdk自己为我们设置成了final的)
                }
            };
            r.run();
    
            System.out.println("----------使用Lambda输出-----------");
    
            Runnable r1 = () -> System.out.println("Hello world!" + num);//省去乐:大括号,分号
            r1.run();
        }
    

    语法格式二:接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)

        @Test
        public void test2(){
            /**
             *语法格式二、
             *  接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)
             */
    
            Consumer<String> con = x -> System.out.println(x);
            con.accept("Lambda牛逼!");
        }
    

    语法格式三:两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return

        @Test
        public void test3(){
            /**
             *语法格式三、
             *  两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return
             */
            Comparator<Integer> com = (x,y) -> {
                System.out.println("函数式接口,");
                return Integer.compare(y,x); //降序
            };
    
            Integer[] nums = {4,2,8,1,5};
            Arrays.sort(nums,com);
            System.out.println(Arrays.toString(nums));
        }
    

    语法格式四:两个参数,有返回值,但是只有一条语句: 大括号省略,return省略

        @Test
        public void test4(){
            /**
             *语法格式四、
             *  两个参数,有返回值,但是只有一条语句: 大括号省略,return省略
             */
            Comparator<Integer> com = (x,y) -> Integer.compare(x,y);//升序
            Integer[] nums = {4,2,8,1,5};
            Arrays.sort(nums,com);
            System.out.println(Arrays.toString(nums));
        }
    

    语法格式五:表达式的参数列表的数据类型 可以省略不写,因为JVM编译器通过上下文推断出数据类型,即"类型推断",

     (Integer x,Integer y ) -> Integer.compare(x,y)
     //可以简写成
     (x,y) -> Integer.compare(x,y);
    

    Lambda 表达式常用示例


    Lambda创建线程


    • 创建 线程对象,然后通过匿名内部类重写 run() 方法,提到匿名内部类我们可以通过使用 lambda 表达式来简化线程的创建过程。

    通过Thread创建线程示例代码:

            Thread t = new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    System.out.println(2 + ":" + i);
                }
            });
            t.start();
    

    通过Runnable创建线程示例代码:

     	/*
         * Lambda实现Runnable接口
         * Runnable 的 lambda表达式,使用块格式,将五行代码转换成单行语句
         */
        @Test
        public  void test6() {
            // 1.1原来方式使用匿名内部类
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello world !使用匿名内部类,开线程");
                }
            }).start();
    
            // 1.2使用 lambda expression
            new Thread(() -> System.out.println("使用 lambda expression,开线程")).start();
    
            // 2.1使用匿名内部类
            Runnable race1 = new Runnable() {
                @Override
                public void run() {
                    System.out.println("使用匿名内部类,不开线程");
                }
            };
    
            // 2.2使用 lambda expression
            Runnable race2 = () -> System.out.println("使用 lambda expression,不开线程");
    
            // 直接调用 run 方法(没开新线程哦!)
            race1.run();
            race2.run();
        }
    

    Lambda遍历集合


    通过调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。

    	//jdk提供的函数式接口
        @FunctionalInterface//这个注解是用判断是否是函数式接口
        public interface Consumer<T> {
            void accept(T t);
            //....
        }
          String[] atp = {"Rafael Nadal", "Novak Djokovic",
                    "Stanislas Wawrinka",
                    "David Ferrer","Roger Federer",
                    "Andy Murray","Tomas Berdych",
                    "Juan Martin Del Potro"};
            List<String> players =  Arrays.asList(atp);
    
            // 以前的循环方式
            for (String player : players) {
                System.out.print(player + "; ");
            }
    
            // 使用 lambda 表达式以及函数操作(functional operation)
            players.forEach((player) -> System.out.print(player + "; "));
    
            // 在 Java 8 中使用双冒号操作符(double colon operator)
            players.forEach(System.out::println);
        }
    

    删除集合中的某个元素


    通过removeIf()方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。

     List<Person> javaProgrammers = new ArrayList<Person>() {
                {
                    add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
                    add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
                    add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
                    add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
                    add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
                    add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
                    add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));
                    add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));
                    add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));
                    add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));
                }
            };
            //removeIf()删除集合中符合条件的值
            javaProgrammers.removeIf(ele -> ele.getSalary() == 2000);
    
            //通过 foreach 遍历,查看是否已经删除
            javaProgrammers.forEach(System.out::println);
    

    集合内元素的排序


    在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。

        /*
         *Lambdas排序集合
         * 在Java中,Comparator 类被用来排序集合。
         */
    		String[] players = {"Rafael Nadal", "Novak Djokovic",
                    "Stanislas Wawrinka", "David Ferrer",
                    "Roger Federer", "Andy Murray",
                    "Tomas Berdych", "Juan Martin Del Potro",
                    "Richard Gasquet", "John Isner"};
            // 1.1 使用匿名内部类根据 name 排序 players
            Arrays.sort(players, new Comparator<String>() {
                @Override
                public int compare(String s1, String s2) {
                    return (s1.compareTo(s2));
                }
            });
            System.out.println("使用静态内部类排序结果:"+Arrays.toString(players));
            System.out.println("-----------------------分割线-------------------------");
    
            String[] players2 = {"Rafael Nadal", "Novak Djokovic",
                    "Stanislas Wawrinka", "David Ferrer",
                    "Roger Federer", "Andy Murray",
                    "Tomas Berdych", "Juan Martin Del Potro",
                    "Richard Gasquet", "John Isner"};
    
            // 1.2 使用 lambda expression 排序 players
            Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
            Arrays.sort(players2, sortByName);
            System.out.println("使用lambda排序结果(方式一):"+Arrays.toString(players2));
            // 1.3 也可以采用如下形式:
            Arrays.sort(players2, (String s1, String s2) -> (s1.compareTo(s2)));
            System.out.println("使用lambda排序结果(方式二):"+Arrays.toString(players2));
    
    

    Lambda 表达式引用方法


    有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。
    语法:
    方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象
    示例代码:

    package com.zy.pagehelper.Interface;
    
    @FunctionalInterface
    public interface ReturnOneParam {
        /**
         *函数式接口
         * 一个参数有返回值
         */
        int method(int a);
    }
    
    public class LambdaTest {
        public static void main(String[] args) {
        	//这里返回函数式接口ReturnOneParam一个参数
            ReturnOneParam lambda1 = a -> doubleNum(a);
            System.out.println(lambda1.method(3));
    
            //lambda2 引用了已经实现的 doubleNum 方法
            ReturnOneParam lambda2 = LambdaTest::doubleNum;
            System.out.println(lambda2.method(3));
    
            LambdaTest exe = new LambdaTest();
    
            //lambda4 引用了已经实现的 addTwo 方法
            ReturnOneParam lambda4 = exe::addTwo;
            System.out.println(lambda4.method(2));
    
        }
    
        /**
         * 要求
         * 1.参数数量和类型要与接口中定义的一致
         * 2.返回值类型要与接口中定义的一致
         */
        public static int doubleNum(int a) {
            return a * 2;
        }
        public int addTwo(int a) {
            return a + 2;
        }
    
    }
    

    构造方法的引用


    一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。

    示例代码:

    
    public interface PersonCreatorBlankConstruct {
            /**接口作为对象的生成器
             *无参构造器
             */
            Person getPerson();
    }
    public interface PersonCreatorParamContruct {
        /*
         *有参构造器
         */
        Person getPerson(String firstName, String lastName, String job,
                       String gender, int age, int salary);
    }
    
    
        /**
         *构造方法的引用
         * 一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。
         * 该接口作为对象的生成器---->创建一个无参构造器,
         * 该接口作为对象的生成器---->创建一个有参构造器,
         */
        @Test
        public  void test9() {
            /**
             *1.lambda表达式创建对象,返回无参函数接口,生参无参对象
             */
            PersonCreatorBlankConstruct creator = () -> new Person();
            Person person = creator.getPerson();
    
            PersonCreatorBlankConstruct creator2 = Person::new;
            Person person1 = creator2.getPerson();
    
            PersonCreatorParamContruct creator3 = Person::new;
            Person person2 = creator3.getPerson("名称", "修改名称","职位","男",23,2000);
        }
    

    完毕!,搞定搞定lambda表达式的基本知识点,接下来我们才可以更深入的认识JDK8的新特性


    参考/推荐博客:
    Lambda表达式详解
    Java8新特性入门一(Lambda表达式一)
    Java Lambda表达式入门

  • 相关阅读:
    hlgoj 1766 Cubing
    Reverse Linked List
    String to Integer
    Bitwise AND of Numbers Range
    Best Time to Buy and Sell Stock III
    First Missing Positive
    Permutation Sequence
    Next Permutation
    Gray Code
    Number of Islands
  • 原文地址:https://www.cnblogs.com/MrYuChen-Blog/p/14000259.html
Copyright © 2011-2022 走看看