JDK新特性-Lambda表达式的神操作

813 阅读8分钟

一、Lambda表达式的介绍

  • Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达
    式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看
    起来更简洁易懂。Lambda表达式同时还提升了对集合、框架的迭代、
    遍历、过滤数据的操作。
  • lambda表达式可以替代只有一个抽象函数的接口实现,告别匿名内部类,代码看起来更简洁易懂
  • lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作
  • lambda可以极大的减少代码冗余,同时代码的可读性要好过冗长的内部类,匿名类

例如以前我们使用匿名内部类来实现代码:

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("running1 .....");
            }
        };
        runnable.run();

使用lambda表达式实现更简洁的代码:

        Runnable runnable3 = ()-> System.out.println("running2....");
        runnable3.run();

lambda表达式语法:

LambdaParameters -> LambdaBody

在这里插入图片描述
args -> expr或者(object … args)-> {函数式接口抽象方法实现逻辑}

​ 1、()参数的个数,根据函数式接口里面抽象的参数个数来决定,当参数只有一个的时候,()可以省略

​ 2、当expr逻辑非常简单的时候,{}和return可以省略

案例说明:

    public static void main(String[] args) throws Exception {
		Callable<String> c1 = new Callable() {
            @Override
            public String call() throws Exception {
                return "muxiaonong";
            }
        };
        System.out.println(c1.call());

        Callable<String> c2 = ()->{return "muxiaonong2";};
        System.out.println(c2.call());

		//逻辑很简单的时候省略 {} 和 return
        Callable<String> c3 = ()->"muxiaonong3";
        System.out.println(c3.call());
	}

二、Lambda表达式的特点

  • 函数式编程
  • 参数类型自动推断
  • 代码量少,简洁

三、Lambda表达式案例

实现方式列表:

​	    ()->{}
​		()->{System.out.println(1);}
​		()->System.out.println(1)
​		()->{return 100;}
​		()->100
​		()->null
​		(int x)->{return x+1;}
​		(int x)->x+1
​		(x)->x+1
​		x->x+1

案例1:线程实现方式:

    public static void main(String[] args) {
        //匿名内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runing1..........");
            }
        });
        
        //Lambda表达式方式
        new Thread(() -> {System.out.println("runing2.....");}).start();
    }

案例2:集合遍历实现方式

    public static void main(String[] args) {
        List<String> list = Arrays.asList("java","python","scala","javascript");

        //普通匿名内部类方式
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        //Lambda方式
        Collections.sort(list,(a,b) -> a.length() - b.length());
        list.forEach(System.out::println);
    }

四、Lambda表达式的应用场景

重要的事情说三遍:任何有函数式接口的地方 * 3

什么是函数式接口: 只有一个抽象方法(Object类中的方法除外)的接口是函数式接口

五、Lambda表达式实际应用

5.1 无参实体类模拟

模拟数据库连接层:

@FunctionalInterface
public interface StudentDao {
    void insert(Student student);
}

实体类

/** @Author mxn
 * @Description 学生实体类
 * @Date 10:19 2020/11/7
 * @Param 
 * @return 
 **/
public class Student {
}
 public static void main(String[] args) {
        StudentDao sd1 = new StudentDao() {
            @Override
            public void insert(Student student) {
                System.out.println("插入学生1");
            }
        };

        StudentDao sd2 = (student)->{
            System.out.println("student: "+student);
        };

        StudentDao sd3 = (Student student)-> System.out.println("student3:"+student);

        sd1.insert(new Student()); //输出 插入学生1
        sd2.insert(new Student());// 输出
        sd3.insert(new Student());// 输出
    }

5.2 有参实体类模拟

实体类

/** @Author mxn
 * @Description 
 * @Date 10:26 2020/11/7
 * @Param
 * @return
 **/
public class Teacher {
}

接口模拟层

@FunctionalInterface
public interface TeacherDao {
    int get(Teacher teacher);
}

实现层

 public static void main(String[] args) {
        TeacherDao td1 = new TeacherDao() {
            @Override
            public int get(Teacher teacher) {
                return 1;
            }
        };
        TeacherDao td2 = (teacher)->{return 2;};
        TeacherDao td3 = (Teacher teacher)->{return 3;};
        TeacherDao td4 = (teacher)->4;
        TeacherDao td5 = (Teacher teacher)->5;

        System.out.println(td1.get(new Teacher()));//输出 1
        System.out.println(td2.get(new Teacher()));//输出 2
        System.out.println(td3.get(new Teacher()));//输出 3
        System.out.println(td4.get(new Teacher()));//输出 4
        System.out.println(td5.get(new Teacher()));//输出 5
}

六、函数式接口

Supplier:代表一个输出
Consumer:代表一个输入
BiConsumer:代表两个输入
Function:代表一个输入,一个输出(一般输入和输出是不同类型的)
UnaryOperator:代表一个输入,一个输出(输入和输出是相同类型的)
BiFunction:代表两个输入,一个输出(一般输入和输出是不同类型的)
BinaryOperator:代表两个输入,一个输出(输入和输出是相同类型的)

在Java中提供了一系列的函数式接口,用来接受后续传入的逻辑,但是对输入和输出有要求

6.1 Supplier:代表一个输出

 		Supplier<String> s1 = ()->{return "muxiaonong";};
        Supplier<String> s2 = ()->"muxiaonong2";
        System.out.println(s1.get());//输出 muxiaonong
        System.out.println(s2.get());//输出 muxiaonong2

6.2 Consumer:代表一个输入

	    Consumer<String> c11 = (str) -> System.out.println(str);
        c11.accept("beijing");//输出 beijing

6.3 BiConsumer:代表两个输入

		BiFunction<String,String,Integer> bf = (a,b)->a.length()+b.length();
        System.out.println(bf.apply("大吉大利", "今晚吃鸡"));//输出一个字符串长度 8 

6.4 Function:代表一个输入,一个输出

//  Function<String,Integer>  用来接收后面的函数的实现,规定必须有一个输入(String)有一个输出(Integer)
 Function<String,Integer> f1 = (str)->{return str.length();};
 System.out.println(f1.apply("abcdefg"));//输出长度 7 

七、方法的引用

  • 方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用

7.1 方法引用的分类

类型语法对应的lambda表达式
静态方法引用类名::staticMethod(args) -> 类名.staticMethod(args)
实例方法引用inst::instMethod(args) -> inst.instMethod(args)
对象方法引用类名::instMethod(inst,args) -> 类名.instMethod(args)
构造方法引用类名::new(args) -> new 类名(args)

7.2 静态方法引用

  • 静态方法引用: 如果函数式接口的实现恰好可以通过 调用一个静态方法 来实现,那么就可以使用静态方法引用

/**
 * @program: lambda
 * @ClassName Test2
 * @description:
 * @author: muxiaonong
 * @create: 2020-10-28 22:15
 * @Version 1.0
 **/
public class Test2 {
	//无参静态方法
	 static String put(){
	        System.out.println("put.....");
	        return "put";
	    }
	
	//有参静态方法
	public static void getSize(int size){
	        System.out.println(size);
	    }
	
	 //有参 有返回值静态方法
	 public static String toUpperCase(String str){
	        return str.toUpperCase();
	    }
	    
	//两个入参,一个返回值静态方法
    public static Integer getLength(String str,String str2){
        return str.length()+str2.length();
    }
	
	  public static void main(String[] args) {
	    //无参静态方法-普通调用
		System.out.println(put());//输出put
		//无参静态方法-原生调用
		Supplier<String> s1 = ()-> Test2.put();
	    System.out.println(s1.get());//输出put
		//无参静态方法-静态方法引用
		Supplier<String> s2 = Test2::put;
	    System.out.println(s2.get());//输出put
		//无参静态方法-内部类调用
	 	Supplier<String> s3 = Fun::hehe;
	    System.out.println(s3.get()); //输出hehe
		
		// 有参静态方法-静态方法引用
		Consumer<Integer> c1 = Test2::getSize;
	    Consumer<Integer> c2 = (size)-> Test2.getSize(size);
	    c1.accept(123);
	    c2.accept(111);
	
		//有参有返回值静态方法
		Function<String,String> f1 = (str)->str.toUpperCase();
	    Function<String,String> f2 = (str)-> Test2.toUpperCase(str);
	    Function<String,String> f3 = Test2::toUpperCase;
	    Function<String,String> f4 = Test2::toUpperCase;
	
	    System.out.println(f1.apply("abc"));//输出 ABC 
	    System.out.println(f2.apply("abc"));//输出 ABC 
	    System.out.println(f3.apply("abc"));//输出 ABC 
	    System.out.println(f4.apply("abc"));//输出 ABC 

		// 两个参数 一个返回值 函数式接口
 		BiFunction<String,String,Integer> bf = (a, b)->a.length()+b.length();
        BiFunction<String,String,Integer> bf2 = Test2::getLength;
        System.out.println(bf2.apply("abc", "def"));//输出 6
        System.out.println(bf.apply("abc", "def"));//输出 6
	
	}

	//内部类
	class Fun {
	    public static String hehe(){
	        return "hehe";
	    }
	
	    public static String toUpperCase(String str){
	        return str.toUpperCase();
	    }
	
	}

}

7.3 实例方法引用

  • 实例方法引用: 如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用
public class Test3 {
	//实例无参方法
    public String put(){
        return "put...";
    }

	//实例有参方法
    public void getSize(int size){
        System.out.println("size:"+size);
    }

	//实例有参有返回值方法
    public String toUpperCase(String str){
        return  str.toUpperCase();
    }
    public static void main(String[] args) {
    	//实例无参方法返回-普通调用
        System.out.println(new Test3().put());//输出 put...
        Supplier<String> s1 = ()->new Test3().put();
        Supplier<String> s2 = ()->{return new Test3().put();};
        Supplier<String> s3 = new Test3()::put;
        System.out.println(s1.get());//输出 put...
        System.out.println(s2.get());//输出 put...
        System.out.println(s3.get());//输出 put...

        //唯一的创建一个test3对象
        Test3 test = new Test3();

        Consumer<Integer> c1 = (size)->new Test3().getSize(size);
        Consumer<Integer> c2 = new Test3()::getSize;
        Consumer<Integer> c3 = test::getSize;


        c1.accept(123);//输出 size:123
        c2.accept(123);//输出 size:123
        c3.accept(123);//输出 size:123

        Function<String,String> f1 = (str)->str.toUpperCase();
        Function<String,String> f2 = (str)->test.toUpperCase(str);
        Function<String,String> f3 = new Test3()::toUpperCase;
        Function<String,String> f4 = test::toUpperCase;
        System.out.println(f1.apply("abc"));//输出 ABC
        System.out.println(f2.apply("abc"));//输出 ABC
        System.out.println(f3.apply("abc"));//输出 ABC
        System.out.println(f4.apply("abc"));//输出 ABC
    }
}

7.4 对象方法引用

  • 对象方法引用: 抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用
/** @Author mxn
 * @Description //TODO 对象方法引用
 * @Date 14:26 2020/11/7
 * @Param 
 * @return 
 **/
public class Test4 {
    public static void main(String[] args) {
        Consumer<Too> c1 = (too)->new Too().foo();
        c1.accept(new Too());//输出 foo

        Consumer<Too> c2 = (Too too) ->new Too2().foo();
        c2.accept(new Too());//输出 foo---too2

        Consumer<Too> c3 = Too::foo;
        c3.accept(new Too());//输出 foo

        BiConsumer<Too2,String> bc = (too2,str)->new Too2().show(str);
        BiConsumer<Too2,String> bc2 = Too2::show;
        bc.accept(new Too2(),"abc");
        bc2.accept(new Too2(),"def");

        BiFunction<Exec,String,Integer> bf1 = (e,s)->new Exec().test(s);
        bf1.apply(new Exec(),"abc");
        BiFunction<Exec,String,Integer> bf2 = Exec::test;
        bf2.apply(new Exec(),"def");
    }
}

class Exec{
    public int test(String name){
        return 1;
    }

}

class Too{
    public Integer fun(String s){
        return 1;
    }
    public void foo(){
        System.out.println("foo");
    }
}
class Too2{
    public Integer fun(String s){
        return 1;
    }
    public void foo(){
        System.out.println("foo---too2");
    }

    public void show(String str){
        System.out.println("show ---too2"+str);
    }
}

7.5 构造方法引用

  • 构造方法引用: 如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用
/** @Author mxn
 * @Description //TODO 构造方法引用
 * @Date 14:27 2020/11/7
 * @Param 
 * @return 
 **/
public class Test5 {
    public static void main(String[] args) {
        Supplier<Person> s1 = ()->new Person();
        s1.get();//输出 调用无参的构造方法
        Supplier<Person> s2 = Person::new;
        s2.get();//输出 调用无参的构造方法

        Supplier<List> s3 = ArrayList::new;
        Supplier<Set> s4 = HashSet::new;
        Supplier<Thread> s5 = Thread::new;
        Supplier<String> s6 = String::new;

        Consumer<Integer> c1 = (age)->new Account(age);
        Consumer<Integer> c2 = Account::new;
        c1.accept(123);// 输出 age 参数构造123
        c2.accept(456);//输出 age 参数构造456

        Function<String,Account> f1 = (str)->new Account(str);
        Function<String,Account> f2 = Account::new;
        f1.apply("abc");//输出 str 参数构造abc
        f2.apply("def");//输出 str 参数构造def

    }
}

class Account{
    public Account(){
        System.out.println("调用无参构造方法");
    }

    public Account(int age){
        System.out.println("age 参数构造" +age);
    }

    public Account(String str){
        System.out.println("str 参数构造" +str);
    }
}

class Person{
    public Person(){
        System.out.println("调用无参的构造方法");
    }
}

八、小结

  • JAVA 8 引入 Lambda表达式是接收了函数式编程语言的思想,和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
  • lambda表达式可以使代码看起来简洁,但一定程度上增加了代码的可读性以及调试的复杂性,所以在使用时应尽量是团队都熟悉使用,要么干脆就别用,不然维护起来是件较痛苦的事,今天的小知识就到这里了,有问题的小伙伴可以在下方进行留言,大家加油!