Lambda表达式

一、Lambda表达式简介

什么是Lambda?

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

为什么使用Lambda

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

Lambda对接口的要求

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

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

@FunctionalInterface

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

二、Lambda的基础语法

1.语法

// 1.Lambda表达式的基础语法
// Lambda是一个匿名函数 一般关注的是以下两个重点
// 参数列表 方法体

/**
* ():用来描述参数列表
* {}:用来描述方法体 有时可以省略
* ->: Lambda运算符 读作goes to
* 例 Test t=()->{System.out.println("hello word")}; 大括号可省略
*/

 

2.创建多个接口

/**
* 无参数无返回值接口
* @author Alan
* @version 1.0
* @date 2020-05-27 10:24
*/
@FunctionalInterface
public interface LambdaNoneReturnNoneParmeter {

   void test();
}

/**
* 无返回值有单个参数
* @author Alan
* @version 1.0
* @date 2020-05-27 10:26
*/
@FunctionalInterface
public interface LambdaNoneReturnSingleParmeter {

   void test(int n);
}

/**
* 无返回值 多个参数的接口
* @author Alan
* @version 1.0
* @date 2020-05-27 10:27
*/
@FunctionalInterface
public interface LambdaNoneReturnMutipleParmeter {

   void test(int a,int b);
}

/**
* 有返回值 无参数接口
* @author Alan
* @version 1.0
* @date 2020-05-27 10:28
*/
@FunctionalInterface
public interface LambdaSingleReturnNoneParmeter {

   int test();
}

/**
* 有返回值 有单个参数的接口
* @author Alan
* @version 1.0
* @date 2020-05-27 10:29
*/
@FunctionalInterface
public interface LambdaSingleReturnSingleParmeter {

   int test(int n);
}

/**
* 有返回值 有多个参数的接口
* @author Alan
* @version 1.0
* @date 2020-05-27 10:30
*/
@FunctionalInterface
public interface LambdaSingleReturnMutipleParmeter {

   int test(int a,int b);
}

3.创建测试类

package com.alan.learn.syntax;

import com.alan.learn.interfaces.*;

/**
* @author Alan
* @version 1.0
* @date 2020-05-27 10:33
*/
public class Syntax1 {

   public static void main(String[] args) {
       // 1.Lambda表达式的基础语法
       // Lambda是一个匿名函数 一般关注的是以下两个重点
       // 参数列表 方法体

       /**
        * ():用来描述参数列表
        * {}:用来描述方法体
        * ->: Lambda运算符 读作goes to
        */

       // 无参无返回  
       LambdaNoneReturnNoneParmeter lambda1=()->{
           System.out.println("hello word");
      };
       lambda1.test();

       // 无返回值 单个参数
       LambdaNoneReturnSingleParmeter lambda2=(int n)->{
           System.out.println("参数是:"+n);
      };
       lambda2.test(10);

       // 无返回值 多个参数
       LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{
           System.out.println("参数和是:"+(a+b));
      };
       lambda3.test(10,12);

       // 有返回值 无参数
       LambdaSingleReturnNoneParmeter lambda4=()->{
           System.out.println("lambda4:");
           return 100;
      };
       int ret=lambda4.test();
       System.out.println("返回值是:"+ret);

       // 有返回值 单个参数
       LambdaSingleReturnSingleParmeter lambda5=(int a)->{
           return a*2;
      };
       int ret2= lambda5.test(3);
       System.out.println("单个参数,lambda5返回值是:"+ret2);

       //有返回值 多个参数
       LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{
           return a+b;
      };
       int ret3=lambda6.test(12,14);
       System.out.println("多个参数,lambda6返回值是:"+ret3);
  }
}

输出结果:
   hello word
参数是:10
参数和是:22
lambda4:
返回值是:100
单个参数,lambda5返回值是:6
   多个参数,lambda6返回值是:26

三、语法精简

针对上述基础语法的精简

1.参数类型精简

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

2.参数小括号精简

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

3.方法大括号精简

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

4.大括号精简补充

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

5.多参数,有返回值 精简

LambdaSingleReturnNoneParmeter lambda4=(a,b)->{
   return a+b;
};
可以精简为:
LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;

四、Lambda语法进阶

1.方法引用(普通方法与静态方法)

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

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

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

语法:

 

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

例:

  • package com.alan.learn.syntax;

    import com.alan.learn.interfaces.LambdaSingleReturnSingleParmeter;

    /**
    * @author Alan
    * @version 1.0
    * @date 2020-05-27 11:48
    */
    public class Syntax3 {

       public static void main(String[] args) {
           
           LambdaSingleReturnSingleParmeter lambda1=a->a*2;
           LambdaSingleReturnSingleParmeter lambda2=a->a*2;
           LambdaSingleReturnSingleParmeter lambda3=a->a*2;

           //简化
           LambdaSingleReturnSingleParmeter lambda4=a->change(a);

           //方法引用
           LambdaSingleReturnSingleParmeter lambda5=Syntax3::change;
      }

       /**
       * 自定义的实现方法
       */
       private static int change(int a){
           return a*2;
      }
    }

2.方法引用(构造方法)

目前有一个实体类

public class Person {
   public String name;
   public int age;

   public Person() {
       System.out.println("Person的无参构造方法执行");
  }

   public Person(String name, int age) {
       this.name = name;
       this.age = age;
       System.out.println("Person的有参构造方法执行");
  }
}

需求

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

interface PersonCreater{
   //通过Person的无参构造实现
   Person getPerson();
}

interface PersonCreater2{
   //通过Person的有参构造实现
   Person getPerson(String name,int age);
}

那么可以写作:

public class Syntax4 {
   public static void main(String[] args) {

       PersonCreater creater=()->new Person();

       //引用的是Person的无参构造
        //PersonCreater接口的方法指向的是Person的方法
       PersonCreater creater1=Person::new; //等价于上面的()->new Person()
       //实际调用的是Person的无参构造 相当于把接口里的getPerson()重写成new Person()。
       Person a=creater1.getPerson();

       //引用的是Person的有参构造
       PersonCreater2 creater2=Person::new;
       Person b=creater2.getPerson("张三",18);
  }
}

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

五、综合练习

1.集合排序案例

package com.alan.exercise;

import com.alan.learn.data.Person;

import java.util.ArrayList;

/**
* 集合排序案例
* @author Alan
* @version 1.0
* @date 2020-05-27 15:08
*/
public class Exercise1 {

   public static void main(String[] args) {

       //需求:已知在一个ArrayList中有若干各Person对象,将这些Person对象按照年龄进行降序排列
       ArrayList<Person> list=new ArrayList<>();


       list.add(new Person("张三",10));
       list.add(new Person("李四",12));
       list.add(new Person("王五",13));
       list.add(new Person("赵六",14));
       list.add(new Person("李雷",11));
       list.add(new Person("韩梅梅",8));
       list.add(new Person("jack",10));

       System.out.println("排序前:"+list);

       //将排列的依据传入 具体的方法指向的是 内部元素的age相减 sort会依据结果的正负进行降序排列
       //sort 使用提供的 Comparator对此列表进行排序以比较元素。
       list.sort((o1, o2) -> o2.age-o1.age);

       System.out.println("排序后:"+list);
  }
}

2.Treeset排序案例

package com.alan.exercise;

import com.alan.learn.data.Person;

import java.util.TreeSet;

/**
* @author Alan
* @version 1.0
* @date 2020-05-27 15:37
*/
public class Exercise2 {
   public static void main(String[] args) {

       /**Treeset 自带排序
        * 但是现在不知道Person谁大谁小无法排序
        * 解决方法:
        * 使用Lambda表达式实现Comparator接口,并实例化一个TreeSet对象
        * 注意:在TreeSet中如果Comparator返回值是 0 会判断这是两个元素是相同的 会进行去重
        * TreeSet<Person> set=new TreeSet<>((o1, o2) -> o2.age-o1.age);
        * 这个获取的对象打印会少一个Person
        * 此时我们将方法修改
       */
       TreeSet<Person> set=new TreeSet<>((o1, o2) ->{
           if(o1.age>=o2.age){
               return -1;
          }else {
               return 1;
          }
      });

       set.add(new Person("张三",10));
       set.add(new Person("李四",12));
       set.add(new Person("王五",13));
       set.add(new Person("赵六",14));
       set.add(new Person("李雷",11));
       set.add(new Person("韩梅梅",8));
       set.add(new Person("jack",10));

       System.out.println(set);
  }
}

3.集合的遍历

package com.alan.exercise;

import java.util.ArrayList;
import java.util.Collections;

/**
* 集合的遍历
* @author Alan
* @version 1.0
* @date 2020-05-27 15:52
*/
public class Exercise3 {

   public static void main(String[] args) {
       ArrayList<Integer> list=new ArrayList<>();

       Collections.addAll(list,1,2,3,4,5,6,7,8,9);
       /**
        * list.forEach(Consumer<? super E> action)
        * api文档解释: 对 集合中的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
        * 将集合中的每一个元素都带入到接口Consumer的方法accept中 然后方法accept指向我们的引用
        * 输出集合中的所有元素
        * list.forEach(System.out::println);
       */

       //输出集合中所有的偶数
       list.forEach(ele->{
           if(ele%2==0){
               System.out.println(ele);
          }
      });
  }
}

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

package com.alan.exercise;

import com.alan.learn.data.Person;

import java.util.ArrayList;

/**
* 删除集合中满足条件的元素
* @author Alan
* @version 1.0
* @date 2020-05-27 16:05
*/
public class Exercise4 {

   public static void main(String[] args) {
       ArrayList<Person> list=new ArrayList<>();

       list.add(new Person("张三",10));
       list.add(new Person("李四",12));
       list.add(new Person("王五",13));
       list.add(new Person("赵六",14));
       list.add(new Person("李雷",11));
       list.add(new Person("韩梅梅",8));
       list.add(new Person("jack",10));

       //删除集合中年龄大于12的元素
       /**
        * 之前迭代器的做法
        * ListIterator<Person> it = list.listIterator();
        * while (it.hasNext()){
        *   Person ele=it.next();
        *   if(ele.age>12){
        *         it.remove();
        *   }
        * }
        */

       /**
        * lambda实现
        * 逻辑
        * 将集合中的每一个元素都带入到接口Predicate的test方法中,
        * 如果返回值是true,则删除这个元素
       */
       list.removeIf(ele->ele.age>10);
       System.out.println(list);
  }
}

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

package com.alan.exercise;

/**
* 需求:
* 开辟一条线程 做一个数字的输出
* @author Alan
* @version 1.0
* @date 2020-05-27 16:17
*/
public class Exercise5 {
   public static void main(String[] args) {

       /**
        * 通过Runnable 来实例化线程
        */
       Thread t=new Thread(()->{
           for(int i=0;i<100;i++){
               System.out.println(i);
          }
      });
       t.start();
  }
}

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

package com.alan.functional;

import java.util.function.*;

/**
* 系统内置的一些函数式接口
* @author Alan
* @version 1.0
* @date 2020-05-27 16:23
*/
public class FunctionalInterface {
   public static void main(String[] args) {

       // Predicate<T>             :     参数是T 返回值boolean  
       // 在后续如果一个接口需要指定类型的参数,返回boolean时可以指向 Predicate
       //         IntPredicate           int -> boolean
       //         LongPredicate           long -> boolean
       //         DoublePredicate         double -> boolean

       // Consumer<T>               :     参数是T 无返回值(void)
       //         IntConsumer             int ->void
       //         LongConsumer           long ->void
       //         DoubleConsumer         double ->void

       // Function<T,R>             :     参数类型T 返回值R
       //         IntFunction<R>         int -> R
       //         LongFunction<R>         long -> R
       //         DoubleFunction<R>       double -> R
       //         IntToLongFunction       int -> long
       //         IntToDoubleFunction     int -> double
       //         LongToIntFunction       long -> int
       //         LongToDoubleFunction   long -> double
       //         DoubleToLongFunction   double -> long
       //         DoubleToIntFunction     double -> int

       // Supplier<T> : 参数 无 返回值T
       // UnaryOperator<T> :参数T 返回值 T
       // BiFunction<T,U,R> : 参数 T、U 返回值 R
       // BinaryOperator<T> :参数 T、T 返回值 T
       // BiPredicate<T,U> : 参数T、U 返回值 boolean
       // BiConsumer<T,U> :   参数T、U 无返回值

       /**
        * 常用的 函数式接口
        * Predicate<T>、Consumer<T>、Function<T,R>、Supplier<T>
        */
  }
}

七、Lambda闭包

package com.alan.closure;

import java.util.function.Supplier;

/**
* @author Alan
* @version 1.0
* @date 2020-05-27 16:59
*/
public class ClosureDemo {
   public static void main(String[] args) {

       /**
        * lambda的闭包会提升包围变量的生命周期
        * 所以局部变量 num在getNumber()方法内被 get()引用 不会在getNumber()方法执行后销毁
        * 这种方法可以在外部获取到某一个方法的局部变量
        */
       int n=getNumber().get();
       System.out.println(n);
  }
   private static Supplier<Integer> getNumber(){
       int num=10;
       /**
        * Supplier supplier=()->num;
        * return supplier;
        */
       return ()->{
           return num;
      };
  }
}
*************************************************************************
   
package com.alan.closure;

import java.util.function.Consumer;

/**
* @author Alan
* @version 1.0
* @date 2020-05-27 17:20
*/
public class ClosureDemo2 {
   public static void main(String[] args) {
       int a=10;
       Consumer<Integer> c=ele->{
           System.out.println(a+1);
           //System.out.println(ele);
           //System.out.println(a++); 会报错
           //在lambda中引用局部变量 这个变量必须是一个常量
      };
       //a++; 这样也会导致内部报错
       //如果在内部已经引用局部变量 参数传递后 打印的还是 10
       c.accept(1);
  }
}

 

posted @ 2021-09-19 16:12  你们都不上班吗  阅读(32)  评论(0)    收藏  举报