Java8新特性:Lambda表达式详解及四大函数式接口

目录

    • 一、Lambda 简介
    • 二、Lambda四大语法特征
    • 三、Lambda四大内置核心函数式接口
      • 3.1、消费型接口:Consumer
      • 3.2、供给型接口:Supplier
      • 3.3、函数型接口:Function
      • 3.4、断言型接口:Predicate
    • 结语

一、Lambda 简介

Lambda 表达式是Java8以后的新特性,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。我们以线程(实现Runnable接口)为例,,看使用Lambda和不适用Lambda的区别。

package com.alian.csdn.lambda;

import org.junit.Test;

public class ThreadWithLambdaTest {

	/**
	 * Java 8之前实现一个线程的简单写法
	 * 此处单元测试的注解是采用:org.junit.Test
	 */
    @Test
    public void oldThread() {
        // Java 8之前实现一个线程的写法
        final String str = "before Java 8";
        new Thread(new Runnable() {
            @Override
            public void run() {
                doSomeThing(str);
            }
        }).start();
    }

	/**
	 * Java 8开始使用Lambda实现一个线程的简单写法
	 * 此处单元测试的注解是采用:org.junit.Test
	 */
    @Test
    public void newThread() {
        final String str = "Lambda";
        //Java 8开始使用Lambda:相当于自动重写了run方法
        new Thread(() -> doSomeThing(str)).start();
    }

    private void doSomeThing(String str) {
        System.out.println("1.执行开始");
        System.out.println("2.执行线程,来源于:" + str);
        System.out.println("3.执行结束");
    }
}

Java 8开始使用Lambda:相当于自动重写了run方法,与原来的版本写法相比,Lambda写法简单( 更适合匿名函数或匿名内部类),代码量更少,但是如果不理解写法的话,同时也会增加团队里代码可读性。

二、Lambda四大语法特征

  • 可选的类型声明:不需要声明参数类型,编译器可以统一识别参数值
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数就需要定义圆括号
  • 可选的大括号:如果主体包含了一个语句,可以不使用大括号
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

四大语法特征的详细的说明如下:

package com.alian.csdn.lambda;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;

public class LambdaTest {

    @Test
    public void lambda() {
        //定义一个列表
        List<String> listStr = Arrays.asList("bbb", "ddd", "cccc", "aa");

        //1.此处p的类型就是由编译器推理得到的,不需要声明类型,当然也可以声明类型,你可以换成你想取的任意的合规名称
        listStr.forEach(p -> System.out.println("1.列表里的值:" + p));

        //2.可选的参数圆括号(无圆括号),一个参数可以不写圆括号
        listStr.forEach(p -> System.out.println("2.1列表里的值(无圆括号):" + p));
        //2.可选的参数圆括号(有圆括号)
        listStr.forEach((p) -> System.out.println("2.2列表里的值(有圆括号):" + p));
        //2.可选的参数圆括号,两个及以上的参数需要加圆括号
        listStr.sort((e1, e2) -> {
            System.out.println("两个及以上的参数需要加圆括号");
            return e1.compareTo(e2);
        });

        //3.可选的大括号(无大括号):如果主体只包含了一个语句,可以不使用大括号
        listStr.forEach(p -> System.out.println("3.1列表里的值(无大括号):" + p));
        //3.可选的大括号(有大括号)
        listStr.forEach(p -> {
            System.out.println("3.1列表里的值(有大括号):" + p);
        });
        //3.可选的大括号,两个及以上语句需要用大括号
        listStr.forEach(p -> {
            System.out.print("休息下:");
            System.out.println("3.1列表里的值(有大括号):" + p);
        });

        //4.可选的返回关键字(一个表达式编译器会自动返回值)
        listStr.sort(String::compareTo);
        //4.可选的返回关键字(大括号需要指明表达式返回了一个数值,一般是多条语句使用大括号)
        listStr.sort((e1, e2) -> {
            System.out.println("大括号需要指明表达式返回了一个数值");
            return e1.compareTo(e2);
        });

    }
}

三、Lambda四大内置核心函数式接口

在本文测试类的同级目录下的Employee类 ,用于接下里的测试

package com.alian.csdn.lambda;

import lombok.Data;

/**
 * 省篇幅,偷懒就用@Data完事算了!
 */
@Data
public class Employee {

    private String id;//员工ID

    private String name;//员工姓名

    private String department;//部门

    private int age;//员工年龄

    private double salary;//工资
    
    /*
     *  简单的构造方法用于测试
     */
    public Employee(String id, String name, String department, int age, double salary) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.age = age;
        this.salary = salary;
    }
}

3.1、消费型接口:Consumer

消费型接口就是传进去参数经过表达式处理,没有返回值

Consumer>:void accept(T t);

3.1.1、源码

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

3.1.2、实战

我们从上面也看到了消费型接口Consumer,接收的是一个泛型,可以是String,Integer,对象(比如Employee)等等,大胆发挥你的想象即可,不要被网上的事例限制了思维,认为只有String等简单类型

package com.alian.csdn.lambda;

import org.junit.Test;
import java.util.function.Consumer;

public class ConsumerTest {

    /**
     * 消费型接口:Consumer
     * 此处单元测试的注解是采用:org.junit.Test
     */
    @Test
    public void consumer() {
        System.out.println("-----------------Consumer------------------------");
        //消费型接口直接传入参数,进行相关处理,没有返回值
        consumerString("我是消费型接口", m -> {
            System.out.println("1.我进入到方法体");
            System.out.println("1.传入的参数输出:" + m);
        });
        System.out.println("-----------------Consumer的缩略写法------------------------");
        //传入参数未参与运算
        consumerString("2.我是消费型接口,缩略写法", System.out::println);
        System.out.println("-----------------Consumer------------------------");
        //我们有一个员工,现在把他调到 "销售二部" 同时 "加200元工资"
        Employee employee = new Employee("BAT001", "胡昊天", "销售一部", 28, 3500);//在本类的同级目录下,见上文
        consumerObeject(employee, m -> {
            m.setDepartment("销售二部");//修改部门
            m.setSalary(3700);//修改工资
            System.out.println("员工信息:" + m);
        });
    }

    public void consumerString(String str, Consumer<String> con) {
        con.accept(str);
    }

    public void consumerObeject(Employee emp, Consumer<Employee> con) {
        con.accept(emp);
    }

}

运行结果:

-----------------Consumer---------------------------------
1.我进入到方法体
1.传入的参数输出:我是消费型接口
-----------------Consumer的缩略写法------------------------
2.我是消费型接口,缩略写法
-----------------Consumer-------------------------------
员工信息:Employee(id=BAT001, name=胡昊天, department=销售二部, age=28, salary=3700.0)

3.2、供给型接口:Supplier

供给型接口就是无中生有,不传参经过表达式处理返回值,比如生成随机数
Supplier>:T get();

3.2.1、源码

package java.util.function;

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

3.2.2、实战

package com.alian.csdn.lambda;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

public class SupplierTest {

    /**
     * 供给型接口:Supplier
     * 此处单元测试的注解是采用:org.junit.Test
     */
    @Test
    public void supplier() {
        //生成m到n随机数,包含m和n ,比如生成10个  15到25的随机数
        List<Integer> numList = getNumList(10, () -> {
            //第一步算出 m-n的值,假设等于w
            //第二步Math.random()*w
            //第三步Math.random()*w+n
            //第四步Math.round(Math.random()w+n)
            return (int) Math.round(Math.random() * (25 - 15) + 15);
        });
        System.out.println("获取到的10个[10,15]的随机数:"+numList);
    }

    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(sup.get());
        }
        return list;
    }
}

运行结果:

获取到的10个[10,15]的随机数:[24, 16, 18, 23, 22, 20, 19, 24, 23, 19]

3.3、函数型接口:Function

函数型接口就是传进去参数经过表达式处理后,有返回值。
Function>:R apply(T t);

3.3.1、源码

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

3.3.2、实战

package com.alian.csdn.lambda;

import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;

public class FunctionTest {

    /**
     * 函数型接口:Function
     * 此处单元测试的注解是采用:org.junit.Test
     */
    @Test
    public void function() {
        System.out.println("------------------Function----------------------------------------");
        String newStr = functionStr("csdnAlian_1223iszhebestman", (str) -> str.substring(4, 14));
        System.out.println("字符串裁剪得到的结果:" + newStr);
        System.out.println("------------------Function,Employee>------------------------------");
        Employee[] employeeArray = {
                new Employee("BAT005", "梁南生", "研发部", 27, 8000),
                new Employee("BAT003", "唐二鹏", "研发部", 32, 9900),
                new Employee("BAT004", "王一林", "研发部", 30, 9000),
                new Employee("BAT006", "吴三雅", "财务部", 25, 6000)
        };
        //员工数组转化为List对象
        List<Employee> list = Arrays.asList(employeeArray);
        //从列表中找到最大年龄员工的信息
        Employee employee = functionObject(list, emp -> {
            //根据年龄进行倒序排序,默认是升序排序,使用reversed()可以进行倒序排序
            emp.sort(Comparator.comparingInt(Employee::getAge).reversed());
            return emp.get(0);
        });
        System.out.println("从列表中获取到的最大年龄的员工信息:" + employee);
    }

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

    public Employee functionObject(List<Employee> emp, Function<List<Employee>, Employee> fun) {
        return fun.apply(emp);
    }
}

运行结果:

------------------Function---------------------------------------------
字符串裁剪得到的结果:Alian_1223
------------------Function,Employee>-----------------------------------
从列表中获取到的最大年龄的员工信息:Employee(id=BAT003, name=唐二鹏, department=研发部, age=32, salary=9900.0)

3.4、断言型接口:Predicate

断言型接口就是传进去参数经过表达式处理,有返回值(true或者false)
Predicate>:boolean test(T t);

3.4.1、源码

package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

3.4.2、实战

package com.alian.csdn.lambda;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class PredicateTest {

    /**
     * 断言型接口:Predicate
     * 此处单元测试的注解是采用:org.junit.Test
     */
    @Test
    public void predicate() {
        Employee[] employeeArray = {
                new Employee("BAT005", "梁南生", "研发部", 27, 8000),
                new Employee("BAT003", "唐二鹏", "研发部", 32, 9900),
                new Employee("BAT004", "王一林", "研发部", 30, 9000),
                new Employee("BAT006", "吴三雅", "财务部", 25, 6000)
        };
        //员工数组转化为List对象
        List<Employee> list = Arrays.asList(employeeArray);
        //找出列表里员工工资大于6000并且年龄小于31岁的员工的信息
        List<Employee> returnList = filterStr2(list, (emp) -> (emp.getSalary() > 6000 && emp.getAge() < 31));
        for (Employee emp : returnList) {
            System.out.println("工资大于6000并且年龄小于31岁的员工的信息:" + emp);
        }
    }

    public List<Employee> filterStr2(List<Employee> list, Predicate<Employee> pre) {
        List<Employee> newList = new ArrayList<>();
        for (Employee emp : list) {
            if (pre.test(emp)) {
                newList.add(emp);
            }
        }
        return newList;
    }

}

运行结果:

工资大于6000并且年龄小于31岁的员工的信息:Employee(id=BAT005, name=梁南生, department=研发部, age=27, salary=8000.0)
工资大于6000并且年龄小于31岁的员工的信息:Employee(id=BAT004, name=王一林, department=研发部, age=30, salary=9000.0)

结语

以上就是今天要讲的内容,本文仅仅简单介绍了Lambda的四大语法特征,以及四大内置核心函数式接口:Consumer、Supplier、Funciton、Predicate的介绍和使用,同时这一节也是我们学好Java 8 Stream的一个基础,需要大家去仔细查看Java提供的API和不断的实践。如果有什么疑问,欢迎大家评论交流。

你可能感兴趣的:(Java基础实战,lambda,java,后端)