Lambda表达式

为什么使用Lambda表达式:

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。它是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,极大地优化代码结构。

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

Lambda表达式对接口的要求:

Lambda 表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。一般地,函数式接口都使用注解 @FunctionalInterface 修饰,来检查是否是函数式接口。

Lambda表达式的基础语法:

JAVA8 中引入了一个新的操作符  “->” ,该操作符称为 “箭头操作符”“Lambda操作符”

箭头操作符将 Lambda 表达式拆分成两部分:

左侧:Lambda 表达式的参数列表

右侧:Lambda 表达式中所需要执行的功能,即 Lambda 体

示例:

import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
 * Lambda 表达式测试
 */
public class TestLambda1 {

    /**
     * 语法格式一: 无参数,无返回值
     */
    @Test
    public void test1(){
        int num = 0;//jdk 1.7 前,必须是 final,jdk 1.8 以后,无需手动加 final ,默认就是final 的
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!" + num);
            }
        };
        r.run();
        System.out.println("-------------------------------");
        Runnable r1 = () -> System.out.println("Hello Lambda !! " + num);
        r1.run();
    }

    /**
     * 语法格式二:有一个参数,并且无返回值
     */
    @Test
    public void test2(){
        //
        /*
            //用一个Java的工具类作为案例。
            @FunctionalInterface //函数式接口的注解
            public interface Consumer<T> {
                void accept(T t);
            }
        */
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("匿名内部类方式输入的参数:"+s);
            }
        };
        con.accept("Java 是最好的编程语言!");
        System.out.println("-------------------------------");
        Consumer<String> con1 = (s) -> System.out.println("Lambda 方式的输入参数:"+s);
        con1.accept("Java 是最好的编程语言!");

    }
    /**
     * 语法格式三:若只有一个参数,小括号可以省略不写
     */
    @Test
    public void test3(){
        Consumer<String> con1 = s -> System.out.println("Lambda 方式的输入参数:"+s);
        con1.accept("Java 是最好的编程语言!");

    }
    /**
     * 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句,则可以加上 {}
     */
    @Test
    public void test4(){
        Comparator<Integer> com = (x,y) -> {
            System.out.println("Java 是最好的编程语言!");
            return Integer.compare(x, y);
        };
        System.out.println(com.compare(3,4));
    }
    /**
     * 语法格式五:若 Lambda 体中只有一条语句, return 和 {} 都可以省略不写
     */
    @Test
    public void test5(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
        System.out.println(com.compare(3,4));
    }
    /**
     * 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM 编译器通过上下文推断出,数据类型,即“类型推断”
     */
    @Test
    public void test6(){
        Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x, y);
        System.out.println(com.compare(3,4));
    }
}
View Code

 

 类型推断:上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

Lambda表达式的应用示例:

示例1:对传入的参数,进行运算,在实际调用方法的时候才知晓具体做什么运算。

@FunctionalInterface
public interface MyFun {
    /**
     * 根据传入的值,进行运算,返回运算后的值。
     * @param i
     * @return
     */
    public Integer getValue(Integer i);
}


public class TestLambda2 {
    public static void main(String[] args) {
        //先用匿名内部类的方式
        MyFun mf = new MyFun() {
            @Override
            public Integer getValue(Integer i) {
                System.out.println("我是最原始的匿名内部类实现的运算(返回:参数 * 100)...");
                return i * 100;
            }
        };
        //对 20 进行 * 100 的运算
        System.out.println(mf.getValue(20));
        System.out.println("-------------------------------");
        //转换成Lambda表达式方式
        MyFun mf1 = (i) -> {
            System.out.println("Lambda 表达式实现运算(返回:参数 * 100)...");
            return i * 100;
        };
        System.out.println(mf1.getValue(20));

        //Lambda表达式可以用最简便的代码,实现各种运算
        //例:
        MyFun mf2 = i -> i + 2;
        MyFun mf3 = i -> i * 2;
        MyFun mf4 = i -> i / 2;
        MyFun mf5 = i -> i - 2;
        //等等....................
    }
}
View Code

示例2:用 Collections.sort() 方法对集合排序。

import lombok.Data;

@Data
public class Employee{
    private String name;
    private Integer age;
    private Integer gender;
}


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

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

        List<Employee> emps = new ArrayList<>();
        emps.add(new Employee("张三",10,0));
        emps.add(new Employee("李四",13,1));
        emps.add(new Employee("王麻子",8,0));
        emps.add(new Employee("王五",18,0));
        emps.add(new Employee("赵六",13,0));

        //原始方法排序
        Collections.sort(emps, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                //按照年龄排序
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println(emps);

        System.out.println("-------------------------------");
        //Lambda表达式方式
        // 排序1:按照年龄排
        Collections.sort(emps,(o1,o2)->o2.getAge() - o1.getAge());
        System.out.println(emps);
        System.out.println("-------------------------------");
        //排序2:如果年龄相同,则按照性别排序
        Collections.sort(emps,(o1,o2)->{
            if((o1.getAge() - o2.getAge()) == 0){
                return o2.getGender() - o1.getGender();
            }
            return o1.getAge() - o2.getAge();
        });
        System.out.println(emps);

    }
}
View Code

 

当我们需要用Lambda表达式来实现一个自己的需求,就得自己去创建一个接口,这样还是比较麻烦的。实际上,我们完全可以不用自己创建接口,使用Java8自带的函数式接口来满足我们的需求。

JAVA8内置的四大函数式接口:

函数式接口  参数类型 返回类型 用途

Consumer<T>

消费型接口

T void 对类型为 T 的对象应用操作,包含方法:void accept(T t);

Supplier<T>

供给型接口

T 返回类型为T 的对象,包含方法:T get();

Function<T,R>

函数型接口

T R 对类型为 T 的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t);

Predicate<T>

断定型接口

T boolean 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t);

 

 

 

 

 

 

 

 

 

 

 

 

 

内置函数式接口示例:

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class TestLambda4 {

    /**
     * Consumer<T> 消费型接口
     */
    @Test
    public void test1(){
        happy(20000.0,m -> System.out.println("你要去做大保健,每次"+m+"元"));
    }
    public void happy(Double money, Consumer<Double> consumer){
        consumer.accept(money);
    }

    /**
     * Supplier<T> 供给型接口
     */
    @Test
    public void test2(){

        List<Integer> list = getNumList(25, () -> (int) (Math.random() * 100));
        System.out.println(list);

    }
    /**
     * 获取指定数量的整数集合
     * @param num
     * @param supplier
     * @return
     */
    public List<Integer> getNumList(int num,Supplier<Integer> supplier){
        List<Integer> list = new ArrayList<>();
        for(int i=0;i<num ;i++){
            Integer result = supplier.get();
            list.add(result);
        }
        return list;
    }

    /**
     * Function<T, R> 函数型接口
     */
    @Test
    public void test3(){
        String newStr = strHandler("\t\t\n Java 好牛逼呀   ", (str) -> str.trim());
        System.out.println(newStr);

        String subStr = strHandler("Java is so so cool...", (str) -> str.toUpperCase());
        System.out.println(subStr);
    }
    /**
     * 需求:用于处理字符串
     * @param str
     * @param fun
     * @return
     */
    public String strHandler(String str, Function<String, String> fun){
        return fun.apply(str);
    }

    /**
     * Predicate<T> 断定型接口
     */
    @Test
    public void test4(){
        List<String> list = Arrays.asList("Java", "Python", "Lambda", "I love u", "ok");
        List<String> strList = filterStr(list, (s) -> s.length() > 4);
        System.out.println(strList);
    }
    /**
     * 返回满足条件的字符串集合
     * @param list
     * @param pre
     * @return
     */
    public List<String> filterStr(List<String> list, Predicate<String> pre){
        List<String> strList = new ArrayList<>();
        for (String str : list) {
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }

}
View Code

 

其他函数式接口:

 

方法引用、构造器引用、数组引用:

方法引用

若 Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”。可以理解为方法引用就是 Lambda 表达式的另外一种表现形式。

方法引用主要的三种语法格式

对象 :: 实例方法名

类 :: 静态方法名

类 :: 实例方法名

注意:

1. Lambda 体中调用方法的 【参数列表与【返回值类型,要与 函数式接口中 抽象方法  的 【参数列表和【返回值类型保持一致。

2. 若 Lambda 参数列表中的 第一参数 是 实例方法的调用者,而 第二个参数 是 实例方法的 参数 时,则可以使用 类 :: 实例方法名(ClassName :: method)

示例:

import org.junit.Test;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class TestLambda5 {
    /**
     * 方法引用测试 对象 :: 实例方法名
     */
    @Test
    public void test1(){
        Consumer<String> con = (str) -> System.out.println("我是参数:"+str);
        con.accept("Hello Lambda");

        Consumer<String> con1 = System.out::println;
        con1.accept("Hello 方法引用...");

        // 需要实现的抽象方法的参数列表和返回值:void accept(String str); accept() 方法的参数为 String ,返回值为 void
        //Lambda体中引用的方法的  参数列表 与 返回值类型 : System.out.println("");System.out 对象的 println() 方法的参数为 String , 返回值为 void
        //在上面这种情况下,(str) -> System.out.println("我是参数:"+str);  就可以直接写成 System.out::println
    }
    /**
     * 方法引用测试 对象 :: 实例方法名
     */
    @Test
    public void test2(){
        Employee emp = new Employee("周三",22,1);
        Supplier<String> sup1 = ()-> emp.getName();
        //Supplier<String> sup1 = emp :: getName; //与上面等价
        System.out.println(sup1.get());
        System.out.println("-------------------------------");
        //Supplier<Integer> sup2 = () -> emp.getAge(); //与下面等价
        Supplier<Integer> sup2 = emp::getAge;
        //sup2.get() 和 emp.getAge() 参数列表都为 Null ,返回值都是Integer ,所以可以用两种表现形式。
        System.out.println(sup2.get());
    }
    /**
     * 方法引用测试 类 :: 静态方法名
     */
    @Test
    public void test3(){
        Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
        System.out.println(com1.compare(10,20));
        System.out.println("-------------------------------");
        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(122,22));
    }
    /**
     * 方法引用测试 类 :: 实例方法名
     */
    @Test
    public void test4(){
        BiPredicate<String,String> bp1 = (x,y) -> x.equals(y);
        System.out.println(bp1.test("abc","abc"));
        System.out.println("-------------------------------");
        BiPredicate<String,String> bp2 = String::equals;
        System.out.println(bp1.test("abc","abc"));
    }
}
View Code

 

构造器引用

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

格式:ClassName :: new

 

注意:

 

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

 

示例:

import org.junit.Test;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 *  构造器 引用 测试
 *  格式:ClassName :: new
 */
public class TestLambda6 {

    /**
     * 调用无参构造器
     */
    @Test
    public void test1(){
        Supplier<Employee> supplier = () -> new Employee();
        System.out.println(supplier.get());
        System.out.println("-------------------------------");
        Supplier<Employee> sup1 = Employee::new;//sup1.get() 没有参数,调用的new Employee() 也是无参构造器。
        System.out.println(sup1.get());
    }
    /**
     * 调用一个参数的构造器
     */
    @Test
    public void test3(){
        Function<String,Employee> fun = (name) -> new Employee(name);
        System.out.println(fun.apply("张麻子"));
        System.out.println("-------------------------------");
        Function<String,Employee> fun1 = Employee::new;//fun1.apply("") 有一个参数,调用的new Employee("")一个参数的构造器。
        System.out.println(fun1.apply("王二麻子"));
    }
    /**
     * 调用两个参数的构造器
     */
    @Test
    public void test4(){
        BiFunction<String,Integer,Employee> fun = (name,age) -> new Employee(name,age);
        System.out.println(fun.apply("张麻子",12));
        System.out.println("-------------------------------");
        BiFunction<String,Integer,Employee> fun1 = Employee::new;//fun1.apply("",int) 有两个参数,调用的new Employee("",int)两个参数的构造器。
        System.out.println(fun1.apply("王二麻子",20));
    }
}
View Code

 

数组引用

格式:type[] :: new

示例:

    /**
     * 数组引用
     */
    @Test
    public void test5() {
        Function<Integer, String[]> fun1 = (x) -> new String[x];
        System.out.println(fun1.apply(10).length);
        System.out.println("-------------------------------");
        Function<Integer, String[]> fun2 = String[]::new;
        System.out.println(fun1.apply(20).length);
    }
View Code

 

posted @ 2020-06-07 18:41  糖油包子  阅读(237)  评论(0编辑  收藏  举报