java8新特性(上)-Lambda表达式

1.Lambda表达式

1.1为什么使用lambda表达式?

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

1.2从匿名类到lambda表达式

  • 原来的内部类
 @Test
    public void test1() {
        Comparator<Integer> com = new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        //TreeSet需要拟传递一个匿名内部类进去,然后按照这个规则进行排序,最后输出的是一个有序的结果
        TreeSet<Integer> ts = new TreeSet<Integer>(com);
        ts.add(15);
        ts.add(20);
        ts.add(18);
        ts.add(30);
        ts.add(49);
        ts.add(16);
        ts.forEach(System.out::println);//输出结果:15 16 18 20 30 49
    }
  • 使用lambda表达式的匿名内部类
@Test
    public void test2() {
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> ts = new TreeSet<Integer>(com);
        ts.add(15);
        ts.add(20);
        ts.add(18);
        ts.add(30);
        ts.add(49);
        ts.add(16);
        ts.forEach(System.out::println);//输出结果:15 16 18 20 30 49
    }

1.3Lmabda表达的优点体验

  • 前置条件,创建一个实体类,以便进行下面实验
    雇员实体类
package entitys;
import lombok.Data;
import java.util.Objects;

@Data
public class Employee {
    private int id;
    private String name;
    private int age;
    private double salary;
    private Status status;
	
	//构造方法
    public Employee() {
    }
	public Employee(int age) {
        this.age = age;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    public Employee(int id, String name, int age, double salary, Status status) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.status = status;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee employee = (Employee) o;
        return getAge() == employee.getAge() && Double.compare(employee.getSalary(), getSalary()) == 0 && Objects.equals(getName(), employee.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge(), getSalary());
    }

    //建立一个枚举
    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
}
  • 使用java自带的方法Arrays.asList把数组转集合,先创建一个eployee实体集合,以便进行实验
List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 99999.9),
            new Employee("李四", 29, 99499.9),
            new Employee("王五", 22, 1999.9),
            new Employee("赵六", 26, 12999.9),
            new Employee("钱七", 38, 3999.9)
    );

需求:获取年龄大于25岁的员工

  • 原来的写法
public List<Employee> filterEmployees(List<Employee> employees) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : employees) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }
  • 改进写法1:使用策略者模式
    先创建一个 Mypredicate的接口,后面可以实现这个接口类的方法
public interface Mypredicate<T> {
    public boolean test(T t);
}

实现Mypredicate接口,通过这个实现接口进行过滤

import entitys.Employee;
//按照年龄进行过滤
public class FilterEmployeeByAge implements Mypredicate<Employee>{

    @Override
    public boolean test(Employee t) {
        return t.getAge()>=20;
    }
}
//按照薪水进行过滤
public class FilterEmployeeBysalary implements Mypredicate<Employee>{
    @Override
    public boolean test(Employee t) {
        return t.getSalary()>=2000;
    }
}

创建一个filterEmployee方法,里面调用Mypredicate的实现方法,进行过滤数据

public List<Employee>filterEmployee(List<Employee>list,Mypredicate<Employee>mp){
        List<Employee>emps=new ArrayList<>();
        for (Employee employee :list) {
            if(mp.test(employee)){
                emps.add(employee);
            }
        }
        return emps;
    }

然后实例化你需要过滤的方式的类,作为参数传递进filterEmployee方法进行过滤.

//按照年龄进行过滤
@Test
    public void test3(){
        //需要传递一个Mypredicate的实现类
        List<Employee>list=filterEmployee(employees,new FilterEmployeeByAge());
        for (Employee employee:list){
            System.out.println(employee);
        }
    }
//按照薪水进行过滤
    @Test
    public void test4(){
        //需要传递一个Mypredicate的实现类
        List<Employee>list=filterEmployee(employees,new FilterEmployeeBysalary());
        //输出金额超过2000的对象
        for (Employee employee:list){
            System.out.println(employee);
        }
    }
  • 改进方式2:使用匿名内部类
 @Test
    public void test5(){
    //这里调用了改进方式1的filterEmployee方法,只是传递进去的参数使用匿名内部类进行实现
        List<Employee>list=filterEmployee(employees, new Mypredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary()<=50000;
            }
        });
        //输出金额超过2000的对象
        for (Employee employee:list){
            System.out.println(employee);
        }
    }
  • 改进方式3:使用lambda表达式
@Test
    public void test6(){
    //这里调用了改进方式1的filterEmployee方法,只是传递进去的参数使用lambda表达式进行实现
        List<Employee>list=filterEmployee(employees,(employee -> employee.getSalary()<=5000));
        list.forEach(System.out::println);
    }
  • 改进方式4:使用Stream API
 @Test
    public void test7(){
        employees.stream()
                .filter((employee -> employee.getSalary()>=5000))//过滤
                .limit(2)//取出前几个
                .forEach(System.out::println);//输出
        System.out.println("-----------------------------------");
        //遍历所有的名字
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);

    }
  • 总结
    上面的所有方法都是逐层递进的关系,可以发现越往后面走,方法越渐变,所写的代码也越来越少,通用性也越来越高,但是存在的问题就是代码的可读性逐渐降低,鱼和熊掌不可兼得,所以选择了效率必定也会有所损失,代码的维护性不高.

1.4 Lambda表达式语法

Lambda表达式在Java语言中引入了一个新的语法元素和操作符。这个操作符为“->”,该操作符被称为Lambda操作符或剪头操作符。它将Lambda分为两个部分:
左侧:指定了Lambda表达式需要的所有参数
右侧:指定了Lambda体,即Lambda表达式要执行的功能。

语法格式一:无参,无返回值,Lambda体只需一条语句

/*语法格式一:无参数,无返回值
                ()->System.out.println("Hello Lambda!");*/
    int num = 0;//在1.7中,匿名内部类使用局部变量,需要加一个final,但是1.8不用加(其实是默认加了,所以不能对这个值进行修改),称之为糖衣于法

    @Test
    public void test1() {
        //以前最常见的就是Runnable()方法
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello lambda" + num);
            }
        };
        r.run();
        //使用lambda表达式的写法
        System.out.println("============================");
        Runnable runnable = () -> System.out.println("Hello lambda" + num);
        runnable.run();
    }

语法格式二:Lambda需要一个参数,此时参数的小括号可以省略

/*语法格式2:有一个参数,无返回值
     * 若只有一个参数,参数的小括号可以省略不写,如果只有一个lambda体,大括号可以省
     * 左右遇一括号省
     * */
    @Test
    public void test2() {
    	//Consumer是java内置的函数式接口
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("加油学习Java");
    }

语法格式三:Lambda需要两个参数,并且有返回值

/*语法格式3:有两个以上的参数,并且lambda体中有多条语句,并且有放回值
     * 如果有多条语句必须使用大括号
     * */
    @Test
    public void test3() {
    //Comparator是java内置的函数式接口
        Comparator<Integer> comparator = (x, y) -> {
            System.out.println("函数式接口");
            return Integer.compare(x, y);
        };
        System.out.println(comparator.compare(15, 20));
    }

语法格式四:当 Lambda 体只有一条语句时,return 与大括号可以省略

 /*语法格式4:有两个以上的参数,并且lambda体中只有一条语句,并且有放回值
     * 则大括号和return都可以省略
     * */
    @Test
    public void test4() {
    //Comparator是java内置的函数式接口
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
        System.out.println(comparator.compare(15, 20));
    }

1.5 推断类型

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

/*语法格式5:lambda表达式的数据源的参数类型可以不写,如果写必须都写
     * 因为JVM编译器通过上下文推断出数据类型,称之为"类型推断"
     * 左侧推断类型省
     * */
    @Test
    public void test5() {
        Comparator<Integer> comparator = (Integer x, Integer y) -> Integer.compare(x, y);
        System.out.println(comparator.compare(45, 20));
    }

2.函数式接口

函数式接口,就是指这个接口只有一个抽象方法的接口,并且在接口类上加上注解:@FunctionalInterface

2.1带有一个参数的函数式接口

  • 例1:使用函数式接口进行数字计算
    创建一个函数式接口
//带有一个抽象方法的接口称之为函数式接口
@FunctionalInterface
public interface MyFun {
    public Integer getValue(Integer num);
}

创建一个方法operation,对函数式接口进行调用

public Integer operation(Integer num, MyFun myFun) {
        return myFun.getValue(num);
    }

使用lambda表达式实现函数式接口

@Test
    public void test6() {
        Integer num = operation(100, (x) -> x * x);
        System.out.println(num);
    }
  • 例2:使用函数式接口对字符串进行操作
    创建一个函数式接口
@FunctionalInterface
public interface MyFunction {
    public String getValue(String str);
}

创建一个方法strHandler,对函数式接口进行调用

public String strHandler(String str,MyFunction mf){
        return mf.getValue(str);
    }

声明函数式接口,接口中声明抽象方法,并对接口进行实现,然后调用strHandler,将接口实现方法传递进去;

 @Test
    public void test2(){
        //替换空格
        String str=strHandler("\t\t\t哈哈,我是大宝贝",(x)->x.trim());
        System.out.println(str);
        System.out.println("-------------------------------");
        //转换成大写
        str=strHandler("happy everyday",x->x.toUpperCase(Locale.ROOT));
        System.out.println(str);
        System.out.println("-------------------------------");
        //截取字符串
        str=strHandler("happy everyday",x->x.substring(2,5));
        System.out.println(str);
    }

2.2带有泛型的函数式接口

创建函数式接口

//泛型类型为<T, R>T为参数,R为返回值
public interface MyFunction2<T, R> {
    public R getValue(T t1, T t2);
}

创建一个方法longHander,对函数式接口进行调用

 public void longHander(Long l1,Long l2,MyFunction2<Long,Long> myFunction){
        System.out.println(myFunction.getValue(l1,l2));
    }

声明函数式接口,接口中声明抽象方法,并对接口进行实现,然后调用strHandler,将接口实现方法传递进去,对两个long型数据进行处理;

@Test
    public void test3(){
        longHander(12L,18L,(x,y)->x+y);
        longHander(12L,18L,(x,y)->x*y);
    }

3.Java 四大核心内置函数式接口

函数式接口参数类型返回类型抽象方法用途
Consumer:消费型接口Tvoidvoid accept(T t);操作数据
Supplier:供给型接口TT get();创造数据
Function<T,R>:函数型接口TRR apply(T t);操作并返回数据
Predicate:断言型函数接口TbooleanBoolean test(T t);判断数据

3.1 Consumer:消费型接口

//Consumer<T>消费型接口
    public void shopping(Double money, Consumer<Double>consumer){
        consumer.accept(money);
    }
    @Test
    public void  test(){
        shopping(1000.0,x-> System.out.println(String.format("今天购物花费了%.2f元",x)));
    }

3.2 Supplier:供给型接口

//Supplier<T>供给型接口
    @Test
    public void test2(){
        //生成10个100以内的随机整数
        List<Integer>list=getNumber(10,()->(int)(Math.random()*100));
        list.forEach(System.out::println);
    }
    //需求:产生指定个数,并且放到集合中
    public List<Integer>getNumber(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.3 Function<T,R>:函数型接口

//Function<T,R>函数型接口
    @Test
    public void test3(){
        String str=strHander("\t\t\t\t\t加油减肥  ",(x)->x.trim());
        System.out.println(str);
        System.out.println("=====================================");
        //截取字符串
        str=strHander("你是笨蛋我是傻瓜",(x)->x.substring(2,5));
        System.out.println(str);
    }

    //需求:用于处理字符串
    public String strHander(String str, Function<String,String>function){
        return function.apply(str);
    }

3.4 Predicate:断言型函数接口

 //Predicate<T>断言型接口:
    @Test
    public void test4(){
        //将长度四个字的名字返回
        String[]names={"辣目洋子","东野圭吾","马尔科夫","史蒂芬","霍金","易烊千玺","雨果","曹禺","钱钟书","张爱玲"};
        List<String>list=new ArrayList<>(Arrays.asList(names));
        List<String>list1=filterStr(list,(x)->x.length()>=4);
        list1.forEach(System.out::println);
    }
    //需求:将满足条件的字符串,放入到集合中
    public List<String> filterStr(List<String>list, Predicate<String>pre){
        List<String>filter_data=new ArrayList<>();
        for(String str:list){
            if(pre.test(str)){
                filter_data.add(str);
            }
        }
        return filter_data;
    }

4.其他接口

函数式接口参数类型返回类型用途
BiFunction<T, U, R>T, UR对类型为 T, U 参数应用操作, 返回 R 类型的结 果。包含方法为R apply(T t, U u);
UnaryOperator (Function子接口)TT对类型为T的对象进行一元运算, 并返回T类型的结果。包含方法为T apply(T t);
BinaryOperator(BiFunction 子接口)T, TT对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1, T t2);
BiConsumer<T, U>T,Uvoid对类型为T, U 参数应用操作。包含方法为void accept(T t, U u);
ToIntFunction ,ToLongFunction ,ToDoubleFunctionTint ,long ,double分 别 计 算 int 、 long 、double、值的函数;
IntFunction, LongFunction, DoubleFunctionint ,long ,doubleR参数分别为int、long、double 类型的函数;

5.方法引用与构造器引用

5.1 方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况:

  • 对象::实例方法
@Test
    public void test2(){
        Employee employee=new Employee();
        Supplier<String>sup=()->employee.getName();
        String str=sup.get();
        System.out.println(str);

        System.out.println("====================使用方法引用====================");
        Supplier<Integer>sup2=employee::getAge;
        System.out.println(sup2.get());
    }
  • 类::静态方法
//类::静态方法名
    @Test
    public void test3(){
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        System.out.println("====================使用方法引用====================");
        BiPredicate<String, String> bp2 = String::equals;
        if(bp2.test("加油","加油")){
            System.out.println("相同");
        }else{
            System.out.println("不同");
        }
    }
  • 类::实例方法
 //对象::实例方法名
    @Test
    public void test1(){
        PrintStream printStream=System.out;
        Consumer<String> con=(x)->printStream.println(x);
        System.out.println("====================使用方法引用====================");
        con=printStream::println;
        //等价于
        con=System.out::println;
        con.accept("abcdefg");
    }

5.2 构造器引用

在实体类中直接创建一个无参构造方法,可以直接通过构造器引用

//构造器引用
    @Test
    public void test5(){
        Supplier<Employee>supplier=()->new Employee();
        //构造引用方法
        Supplier<Employee>sup2=Employee::new;
        Employee emp=sup2.get();
        System.out.println(emp);
    }

    @Test
    public void test6(){
        Function<Integer,Employee>function=(x)->new Employee(x);
        System.out.println("================使用方法引用=================");
        Function<Integer,Employee>fun2=Employee::new;
        Employee employee=fun2.apply(50);
        System.out.println(employee.toString());
        //BiFunction是Function的子类
        BiFunction<String,Integer,Employee>biFunction=Employee::new;
        Employee employee1=biFunction.apply("张三",100);
        System.out.println(employee1.toString());
    }

5.3 数组引用

//数组引用
    @Test
    public void test7(){
        Function<Integer,String[]>fun=(x)->new String[x];
        String[]strs=fun.apply(10);
        System.out.println(strs.length);//20
        //使用数组引用
        Function<Integer,String[]>fun2=String[]::new;
        String[] str2=fun2.apply(20);
        System.out.println(str2.length);//20
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值