一、什么是Lambda表达式?

Lambda表达式是一个匿名函数, 我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递) 。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使得Java语言表达能力得到了提升。、
Java8中引入了一个新的操作符” -> ”该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分为两部分:
()->{}
左侧: Lambda表达式的参数列表。对应接口中抽象方法的参数列表
右侧: Lambda表达式中所需要执行的功能,即Lambda体。对应接口中抽象方法的实现。

1.为什么使用lambda表达式?

使用lamdba表达式可以对一个接口进行非常简洁的实现。
在没有lamdba表达式之前,我们需要这样:

[1]接口实现类对象
interface Fly{ 
	void fly(); 
}
class Bird implements Fly{ 
	@Override public void fly() {
	 	System.out.println("小鸟飞行"); 
	} 
}
class SomethingCanFly implements Fly{ 
@Override
public void fly() { 
	System.out.println("可以飞行"); 
	} 
}
public static void main(String[] args) { 
	Fly fly = new Bird();
	fly.fly(); 
	Fly something = new SomethingCanFly(); 
	something.fly(); 
}

这样做会很麻烦,如果我想要很多个不同的 fly 方法的实现则需要定义很多Fly的实现类,所
以我们可以定义Fly接口的匿名内部实现类,而且是在使用的时候定义,同时创建出匿名内部实现类的对象。

[2]匿名内部类对象

此时只需要有接口,在使用时创建接口的匿名内部类同时创建对象,重写方法即可

public class Demo002 { 
public static void main(String[] args) { 
	Fly something = new Fly(){ 
		@Override public void fly() { 
			System.out.println("飞行"); 
			} 
		};
		something.fly(); 
	} 
}
interface Fly{ 
	void fly(); 
}
[3]Lambda表达式

此时对接口匿名内部类及对象的创建,最终目的只是为了重写接口中的抽象方法,而且抽象方法仅有一个,基于这种情况:
1、前面的接口规定了类型(后面的对象只能 是该接口的实现类对象);
2、接口中只有一个抽象方法(所有的功能都是对该方法的重 写,即方法名等已经固定了)。因此从形式上可以简写为如下形式:

public class Demo002 { 
public static void main(String[] args) { 
	Fly something = ()->{System.out.println("飞行");}; 
	} 
}
interface Fly{ 
	void fly(); 
}

该种形式即为Lambda的写法,其中 Fly something 规定了此时使用的接口类型, () 为参数列表, {System.out.println(“飞行”);} 为方法体, 非常重要的 -> 即为参数列表和方法体的分割符。

二、 Lambda语法

现在我们可以使用Lambda代替创建匿名函数的部分,Lambda的语法如下。

(形参列表)->{操作集合};

注意:Lambda表达式需要函数式接口的支持

1.函数式接口

接口中有且仅有一个抽象方法的接口,称为函数式接口。

  1. default 修饰方法只能在接口中使用(只能被实现了这个接口的对象调用),在接口种被default标记的方法为普通方法,可以直接写方法体。但是有一个抽象方法和多个default标记的方法的接口仍是函数式接口。
  2. 可以使用注解 @FunctionalInterface 修饰,可以检查是否是函数式接口

以前我们整个java的继承关系已经使用了很多年,如果在java新版本中为了迎合函数式接口一个接口中只有一个抽象方法,会导致以前很多接口失效,那么别人也没法进行jdk的升级所以提供了 默认方 法,在jdk1.8之后,可以在接口中定义默认方法。

2. 六种函数式接口

/**
 * 无参无返回值
 */
@FunctionalInterface
public interface NoReturnNoParam {
    void test();
}

/**
 * 单个参数无返回值
 */
@FunctionalInterface
public interface NoReturnSingleParam {
    void test(int a);
}

/**
 * 多个参数无返回值
 */
@FunctionalInterface
public interface NoReturnMupitleParam {
    void test(int a,int b);
}

/**
 * 无参有返回值
 */
@FunctionalInterface
public interface SingleReturnNoParam {
    String test();
}

/**
 * 一个参数,有返回值
 */
@FunctionalInterface
public interface SingleReturnSingleParam {
    int test(int a);
}

/**
 * 多参,有返回值
 */
@FunctionalInterface
public interface SingleReturnMupitleParam {
    int test(int a,int b);
}

3.lamdba表达式的基本用法

/**
 * @author asas
 * lambda表达式基础语法
 */
public class BasicSyntax {
    public static void main(String[] args) {
        //1.实现无参无返回值函数式接口
        NoReturnNoParam nrnp=()->{
            System.out.println("我是无参无返回值的函数式接口");
        };
        nrnp.test();//我是无参无返回值的函数式接口
        //2.实现单参无返回值函数式接口
        NoReturnSingleParam nrsp=(int a)->{
            System.out.println("我是单参无返回值的函数式接口,参数是"+a);
        };
        nrsp.test(4);//我是单参无返回值的函数式接口,参数是4
        //3.实现多参无返回值函数式接口
        NoReturnMupitleParam nrmp=(int a,int b)->{
            System.out.println("我是多参无返回值的接口,两参之和为"+(a+b));
        };
        nrmp.test(4,5);//我是多参无返回值的接口,两参之和为9
        //4.实现无参有返回值函数式接口
        SingleReturnNoParam srnp=()->{
            return "我是无参有返回值函数式接口";
        };
        System.out.println(srnp.test());//我是无参有返回值函数式接口
        //5.实现单参有返回值函数式接口
        SingleReturnSingleParam srsp=(int a)->{
            System.out.print("我是单参有返回值函数式接口");
            return a;
        };
        System.out.println(srsp.test(4));//我是单参有返回值函数式接口4
        //6.实现多参有返回值函数式接口
        SingleReturnMupitleParam srmp=(int a,int b)->{
            System.out.print("我是多参有返回值函数式接口");
            return a+b;
        };
        System.out.println(srmp.test(4, 5));//我是多参有返回值函数式接口9
    }
}

4.方法引用

[1] 类::静态方法名、对象::实例方法名
/**
 * 函数引用
 */
 @FunctionalInterface
 interface Calculate{
    int calculate(int a,int b);
}
public class Lambda1 {

    public static void main(String[] args) {
        //方法的调用,lamdba表达式调用了calculate(int a,int b)方法
        Calculate c=(x,y)->calculate(x,y);
        System.out.println(c.calculate(4, 6));

        //函数引用,引用一个静态方法,引用变量c2指向了Lambda1类下的calculate方法
        Calculate c2=Lambda1::calculate;
        System.out.println(c2.calculate(5, 5));
        //引用一个非静态方法。需要用new 函数名()::方法 来引用
        Calculate c3=new Lambda1()::calculate2;
        System.out.println(c3.calculate(5, 4));
    }

    private static int calculate(int a,int b){
        if (a>b){
            return a-b;
        }else if (a<b){
            return b-a;
        }else {
            return a*b;
        }
    }
    private int calculate2(int a,int b){
        if (a>b){
            return a-b;
        }else {
            return a+b;
        }
    }
}
[2]类::实例方法名

Student类重写了equals方法

class Student{
    private int age;

    public Student(int age) {
        this.age = age;
    }
   
    public boolean equals(Student s){
        if (this.age==s.age){
            return true;
        }else {
            return false;
        }
    }   
}

定义一个函数式接口,用来比较两个对象是否相等

@FunctionalInterface
interface BiPredicate<T,E>{
    boolean equals(T t,E e);
}

具体调用方法

public static void main(String[] args) {
        //lambda表达式引用equals方法
        BiPredicate<Student,Student> b=(x,y)->{return x.equals(y);};
        Student stu=new Student(23);
        Student stu2=new Student(23);
        System.out.println(b.equals(stu, stu2));

        //方法引用
         BiPredicate<Student,Student> b1=Student::equals;
        System.out.println(b1.equals(stu, stu2));
    }

注意:
1、Lambda体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
2、若Lambda参数列表中的第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method。

[3]构造器引用

实体类

class Employee{
    private String name;
    private int age;
    private boolean gender;

    public Employee(String name, int age, boolean gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Employee( int age,String name) {
        this.name = name;
        this.age = age;
    }

    public Employee(int age) {
        this.age = age;
    }

    public Employee() {
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}
@FunctionalInterface
interface Supplier<T>{
    T get();
}

使用Supplier函数式接口引用无参构造

Supplier<Employee> sup = () -> new Employee();
        Employee emp = sup.get();

        // 构造器引用方式
        Supplier<Employee> sup1 = Employee::new;
        Employee emp1 = sup.get();
        System.out.println(emp1);

使用Function函数式接口引用指定的有参构造

Function<Integer, Employee> func = (x) -> new Employee(x);
        func.apply(20); // 需要调用的构造器的参数列表要与函数式接口中抽象方法参数列表保持一致
        Function<Integer, Employee> func2 = Employee::new;
        Employee emp2 = func2.apply(18); System.out.println(emp1);
        BiFunction<Integer, String, Employee> bf = Employee::new;
        Employee emp3 = bf.apply(20, "张三"); System.out.println(emp2);

**注意:**需要调用的构造器的参数列表要与函数式接口中抽象方法参数列表保持一致

三、四大内置函数式接口

Lambda的实现依赖于函数式接口,接口可以我们自己定义,当然也可以使用JDK已经提供好的一些函数式接口,这些接口基本已经能够满足我们的常用操作,并且在集合等框架中已经广泛地使用了,所以我们可以直接使用这些接口。
消费型、供给型、函数型、断定型
消费性:只进不出
供给型:白手起家,空手套白狼
函数型:礼尚往来
断定行:鉴定评审
注意:关注的只有接口中方法的参数和返回值即可

1.Comsumer消费型

消费型接口
Comsumer void accept(T t)
带一个参数,在方法体中使用完了就完了,例如在Collection中的 forEach 方法则需要一个Consumer接口的实现类对象。

Collection<String> list = new ArrayList<>();
        list.add("AAAAAA");
        list.add("BBBBBB");
        list.add("CCCCCC");
        // forEach方法需要一个Consumer接口, 因为Consumer接口里面的accept方法需要带一个参数
        // 将集合中每一个元素, 依次给到 accept方法中, 由你决定,对现在拿到的每一个元素做什么操作
        list.forEach(t-> System.out.println(t));

2. Supplier 供给型接口

/** 
	Supplier<T> 供给型接口 * T get();
 */ 
// 需求:产生指定个数的整数,并放入集合中
/**
 * 供给型接口
 */
public class Lambda5 {
    public static void main(String[] args) {
        List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
        for (Integer integer : numList) { System.out.println(integer);
        }
    }
    public static List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < num; i++) {
            Integer n = sup.get(); list.add(n);
        }
        return list;
    }
}

3. Function <T, R> 函数型接口

/**
 * Function<T, R> 函数型接口
 * R apply(T t);
 */
public class Lambda6 {
    public static void main(String[] args) {
        //需求:用于处理字符串
        String newStr = strHandler(" shsxt ", (str) -> {return str.trim();});
        System.out.println(newStr);
        newStr = strHandler("shsxt", (str) -> str.substring(2, 5));
        System.out.println(newStr);
    }

    public static String strHandler(String str, Function<String, String> fun) {
        return fun.apply(str);
    }
}

4.Predicate 断言型接口

/**
 * 评审型接口|断定型接口 Predicate
 */
public class Lambda7 {
    public static void main(String[] args) {
         /*Predicate<String> p = (str)->{return str.length()>5;};
        System.out.println(p.test("abcdefg"));*/

        List<String> list = new ArrayList<>();
        list.add("aaaa");
        list.add("bbbbbb");
        list.add("ddddd");
        list.add("asdfadf");
        list.add("asdfadf");

        // 挑选出字符个数大于5的元素
        getSome(list,(s)->{return s.length()>5;} ).forEach((t)->{
            System.out.println(t);
        });

        System.out.println("---------------------------");
        getSome(list,(s)->{return s.contains("a");}).forEach((t)-> System.out.println(t));
    }

    // 将List<String>集合中的 满足条件的 元素提取出来
    public static List<String> getSome(List<String> list, Predicate<String> p){
        // 准备一个集合 存储满足条件的元素
        List<String> newList = new ArrayList<>();
        // s 是list中的每一个元素
        list.forEach((s)->{
            // 使用 p对s 进行判定
            if(p.test(s)){
                // 如果满足条件 则添加到新的集合中
                newList.add(s);
            }
        });
        return  newList;
    }
}