Java8新特性:Lambda表达式

文章目录

  • 一、为什么使用 Lambda 表达式
  • 二、Lambda 表达式
    • 1.语法及应用
    • 2.类型推断
    • 3.小结
  • 三、函数式(Functional)接口
    • 1.函数式(Functional)接口
    • 2.如何理解函数式接口
    • 3.Java 内置四大核心函数式接口
    • 4.其他接口
  • 四、方法引用与构造器引用
    • 1.方法引用
    • 2.构造器引用
    • 3.数组引用


一、为什么使用 Lambda 表达式

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

从匿名类到 Lambda 的转换举例1:
Java8新特性:Lambda表达式_第1张图片

从匿名类到 Lambda 的转换举例2:
Java8新特性:Lambda表达式_第2张图片

二、Lambda 表达式

1.语法及应用

Lambda 表达式: 在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “ -> ” , 该操作符被称为 Lambda 操作符 箭头操作符 。它将 Lambda 分为两个部分:
左侧: 指定了 Lambda 表达式需要的 参数列表
右侧: 指定了 Lambda 体 , 是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。

格式 语法
无参,无返回值 Runnable r1 = () -> {System.out.println(“Hello Lambda!”);};
Lambda 需要一个参数,但是没有返回值 Consumer< String > con = < String str > -> {System.out.println(str);};
数据类型可以省略 ,因为可由编译器推断得出,称为“类型推断” Consumer< String > con = (str) -> {System.out.println(str);};
Lambda 若只需要一个参数时, 参数的小括号可以省略 Consumer< String > con = str -> {System.out.println(str);};
Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 Comparator< Integer > com = (x, y) -> {System.out.println(“实现函数式接口方法!”); return Integer.compare(x, y)};
当 Lambda 体只有 一条 语句时, return 与大括号 若有,都可以省略 Comparator< Integer > com = (x, y) -> Integer.compare(x, y);

代码如下(示例):

public class LambdaTest1 {
    // 语法格式一: 无参,无返回值
    @Test
    public void test1() {
        Runnable ri = new Runnable() {
            @Override
            public void run() {
                System.out.println(123456);
            }
        };
        ri.run();

        System.out.println("=============================");

        // Lambda表达式的写法
        Runnable r2 = () -> {
            System.out.println("一二三四五六");
        };
        r2.run();
    }

    // 语法格式二: Lambda 需要一个参数,但是没有返回值。
    @Test
    public void test2() {
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("java8新特性");

        System.out.println("==============================");

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("java8新特性");
    }

    // 语法格式三: 数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test3() {
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("java8新特性");

        System.out.println("==============================");

        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("java8新特性");
    }

    @Test
    public void test4() {
        ArrayList<String> list = new ArrayList<>();  // 类型推断
        int[] arr = {1, 2, 3};  // 类型推断
    }

    // 语法格式四: Lambda 若只需要一个参数时, 参数的小括号可以省略
    @Test
    public void test5() {
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };
        con1.accept("java8新特性");

        System.out.println("==============================");

        Consumer<String> con2 = s -> {
            System.out.println(s);
        };
        con2.accept("java8新特性");
    }

    // 语法格式五: Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test6() {
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };
        System.out.println(com1.compare(12, 23));

        System.out.println("==============================");

        Comparator<Integer> com2 = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(com2.compare(12, 6));
    }

    // 语法格式六: 当 Lambda 体只有一条语句时, return 与大括号若有,都可以省略
    @Test
    public void test7() {
        Comparator<Integer> com1 = (o1, o2) -> {
            return o1.compareTo(o2);
        };
        System.out.println(com1.compare(12, 6));

        System.out.println("==============================");

        Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
        System.out.println(com2.compare(12, 6));
    }

    @Test
    public void test8() {
        Consumer<String> con1 = s -> {
            System.out.println(s);
        };
        con1.accept("java8新特性");

        System.out.println("==============================");

        Consumer<String> con2 = s -> System.out.println(s);
        con2.accept("java8新特性");
    }

}

2.类型推断

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

3.小结

Lambda表达式的使用

  • 1、举例: (o1, o2) -> Integer.compare(o1, o2);
  • 2、格式:
    ⭐️ Lambda操作符 或 箭头操作符
    ⭐️ 左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
    ⭐️ 右边:Lambda体(其实就是重写的抽象方法的方法体)
  • 3、Lambda表达式的使用(分为 6种情况介绍)
    ⭐️ 左边:Lambda形参列表的参数类型可以省略(类型推断);如果Lambda参数列表只有一个参数,其一对()也可以省略。
    ⭐️ 右边:Lambda体应该使用一对{}包裹;如果Lambda体只有一条执行语句(可能是return语句),省略这一对{}和 return关键字。
  • 4、Lambda表达式的本质:作为函数式接口的实例(对象)
  • 5、如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口.
    ⭐️ 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。
  • 6、所以以前用匿名实现类表示的现在都可以用Lambda表达式来写

三、函数式(Functional)接口

1.函数式(Functional)接口

只包含一个抽象方法的接口,称为函数式接口
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口。

2.如何理解函数式接口

Java从诞生日起就是一直倡导“一切皆对象”, 在Java里面面向对象 (OOP) 编程是一切。但是随着python、 scala等语言的兴起和新技术的挑战, Java不得不做出调整以便支持更加广泛的技术要求,也即 java不但可以支持OOP还可以支持OOF(面向函数编程)。
在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中, Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中, Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型—— 函数式接口。
简单的说,在Java8中, Lambda表达式就是一个函数式接口的实例。 这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

/**
 * 自定义函数式接口
 */

@FunctionalInterface
public interface MyInterface {

    void method1();
}

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

函数式接口 参数类型 返回类型 用途
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)

代码如下(示例):

public class LambdaTest2 {
    @Test
    public void test1() {
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("买了瓶矿泉水,花了" + aDouble);
            }
        });

        System.out.println("==========================");

        happyTime(400, money -> System.out.println("买了瓶矿泉水,花了" + money));
    }

    public void happyTime(double money, Consumer<Double> con) {
        con.accept(money);
    }

    @Test
    public void test2() {
        List<String> list = Arrays.asList("北京", "天津", "南京", "西京");
        List<String> lists1 = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });
        System.out.println(lists1);

        System.out.println("===============================");

        List<String> lists2 = filterString(list, s -> s.contains("京"));
        System.out.println(lists2);
    }

    // 根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
    public List<String> filterString(List<String> list, Predicate<String> pre) {
        ArrayList<String> filterList = new ArrayList<>();
        for (String s : list) {
            if (pre.test(s)) {
                filterList.add(s);
            }
        }
        return filterList;
    }

}

4.其他接口

函数式接口 参数类型 返回类型 用途
BiFunction T, U R 对类型为 T, U 参数应用操作, 返回 R 类型的结果。 包含方法为: R apply(T t, U u);
UnaryOperator(Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果。 包含方法 为:T apply(T t);
BinaryOperator(BiFunction 子接口) T,T T 对类型为T的对象应用操作,并返回T类型的结果。包含方法:T apply(T t1 , T t2);
BiConsumer T, U void 对类型为T, U 参数应用操作。包含方法为: void accept(T t, U u)
BiPredicate T, U boolean 包含方法为: boolean test(T t,U u)

四、方法引用与构造器引用

1.方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
使用格式: 类(或对象) :: 方法名
要求: 实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式: 使用操作符 :: 将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:

  • ⭐️ 情况一:对象 :: 实例方法名
  • ⭐️ 情况二:类 :: 静态方法名
  • ⭐️ 情况三:类 :: 实例方法名

方法引用使用的要求:要求接口中抽象方法的形参列表和返回值类型 与 方法引用的方法的形参列表和返回值类型相同!(针对于情况一和情况二)

Java8新特性:Lambda表达式_第4张图片
Java8新特性:Lambda表达式_第5张图片

注意: 当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时: ClassName :: methodName

public class Employee {
	private int id;
	private String name;
	private int age;
	private double salary;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public Employee() {
		System.out.println("Employee().....");
	}

	public Employee(int id) {
		System.out.println("Employee(int id).....");
		this.id = id;
	}

	public Employee(int id, String name) {
		this.id = id;
		this.name = name;
		System.out.println("Employee(int id, String name).....");
	}

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

	@Override
	public String toString() {
		return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		Employee employee = (Employee) o;

		if (id != employee.id)
			return false;
		if (age != employee.age)
			return false;
		if (Double.compare(employee.salary, salary) != 0)
			return false;
		return name != null ? name.equals(employee.name) : employee.name == null;
	}

	@Override
	public int hashCode() {
		int result;
		long temp;
		result = id;
		result = 31 * result + (name != null ? name.hashCode() : 0);
		result = 31 * result + age;
		temp = Double.doubleToLongBits(salary);
		result = 31 * result + (int) (temp ^ (temp >>> 32));
		return result;
	}
}
public class MethodRefTest {

	// 情况一:对象 :: 实例方法
	//Consumer中的void accept(T t)
	//PrintStream中的void println(T t)
	@Test
	public void test1() {
		Consumer<String> con1 = str -> System.out.println(str);
		con1.accept("北京");

		System.out.println("===================");

		PrintStream ps = System.out;
		Consumer<String> con2 = ps :: println;
		con2.accept("西安");
	}
	
	//Supplier中的T get()
	//Employee中的String getName()
	@Test
	public void test2() {
		Employee emp = new Employee(1001, "Tom", 23, 15000);

		Supplier<String> sup1 = () -> emp.getName();
		System.out.println(sup1.get());

		System.out.println("========================");

		Supplier<String> sup2 = emp :: getName;
		System.out.println(sup2.get());
	}

	// 情况二:类 :: 静态方法
	//Comparator中的int compare(T t1,T t2)
	//Integer中的int compare(T t1,T t2)
	@Test
	public void test3() {
		Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
		System.out.println(com1.compare(12, 23));

		System.out.println("========================");

		Comparator<Integer> com2 = Integer :: compare;
		System.out.println(com2.compare(12, 6));
	}
	
	//Function中的R apply(T t)
	//Math中的Long round(Double d)
	@Test
	public void test4() {
		Function<Double, Long> func = new Function<Double, Long>() {
			@Override
			public Long apply(Double d) {
				return Math.round(d);
			}
		};

		System.out.println("========================");

		Function<Double, Long> func1 = d -> Math.round(d);
		System.out.println(func1.apply(12.3));

		System.out.println("========================");

		Function<Double, Long> func2 = Math :: round;
		System.out.println(func2.apply(12.6));
	}

	// 情况三:类 :: 实例方法 
	// Comparator中的int comapre(T t1,T t2)
	// String中的int t1.compareTo(t2)
	@Test
	public void test5() {
		Comparator<String> com = new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		};
		System.out.println(com.compare("abc", "abd"));

		System.out.println("========================");

		Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
		System.out.println(com1.compare("abc", "abd"));

		System.out.println("========================");

		Comparator<String> com2 = String :: compareTo;
		System.out.println(com2.compare("abc", "abc"));
	}

	//BiPredicate中的boolean test(T t1, T t2);
	//String中的boolean t1.equals(t2)
	@Test
	public void test6() {
		BiPredicate<String, String> pre = new BiPredicate<String, String>() {
			@Override
			public boolean test(String s1, String s2) {
				return s1.equals(s2);
			}
		};
		System.out.println(pre.test("abc", "abc"));

		System.out.println("========================");

		BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
		System.out.println(pre1.test("abb", "aab"));

		System.out.println("========================");

		BiPredicate<String, String> pre2 = String :: equals;
		System.out.println(pre2.test("acc", "acv"));
	}
	
	// Function中的R apply(T t)
	// Employee中的String getName();
	@Test
	public void test7() {
		Employee emp = new Employee(1001, "Jzx", 24, 18000);

		Function<Employee, String> func = new Function<Employee, String>() {
			@Override
			public String apply(Employee employee) {
				return employee.getName();
			}
		};
		System.out.println(func.apply(emp));

		System.out.println("========================");

		Function<Employee, String> func1 = e -> e.getName();
		System.out.println(func1.apply(emp));

		System.out.println("========================");

		Function<Employee, String> func2 = Employee :: getName;
		System.out.println(func2.apply(emp));
	}

}

2.构造器引用

格式: ClassName :: new
与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法, 要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
Java8新特性:Lambda表达式_第6张图片

/**
 * 构造器引用:
 *      和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
 *      抽象方法的返回值类型即为构造器所属的类的类型。
 */
public class ConstructorRefTest {
	//构造器引用
    //Supplier中的T get()

    // Employee 的空参构造器 Employee()
    @Test
    public void test1(){
        Supplier<Employee> sup = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };

        System.out.println("=====================");

        Supplier<Employee> sup1 = () -> new Employee();
        Employee employee = sup1.get();
        System.out.println(employee);

        System.out.println("=====================");

        Supplier<Employee> sup2 = Employee::new;
        Employee employee1 = sup2.get();
        System.out.println(employee1);
    }

	//Function中的R apply(T t)
    @Test
    public void test2(){
        Function<Integer, Employee> func = new Function<Integer, Employee>() {
            @Override
            public Employee apply(Integer id) {
                return new Employee(id);
            }
        };

        System.out.println("=====================");

        Function<Integer, Employee> func1 = id -> new Employee(id);
        Employee emp1 = func1.apply(1001);
        System.out.println(emp1);

        System.out.println("=====================");
        Function<Integer, Employee> func2 = Employee::new;
        Employee emp2 = func2.apply(1002);
        System.out.println(emp2);
    }

	//BiFunction中的R apply(T t,U u)
    @Test
    public void test3(){
        BiFunction<Integer, String, Employee> func = new BiFunction<Integer, String, Employee>() {
            @Override
            public Employee apply(Integer id, String name) {
                return new Employee(1001, "Tom");
            }
        };

        System.out.println("=====================");

        BiFunction<Integer, String, Employee> func1 = (id, name) -> new Employee(id, name);
        Employee e1 = func1.apply(1001, "Tom");
        System.out.println(e1);

        System.out.println("=====================");
        BiFunction<Integer, String, Employee> func2 = Employee::new;
        Employee e2 = func2.apply(1002, "Terry");
        System.out.println(e2);
    }

}

3.数组引用

格式: type[] :: new
Java8新特性:Lambda表达式_第7张图片

/**
 * 数组引用:可以把数组看做是一个特殊的类,则写法就与构造器引用一致了。
 */
public class ConstructorRefTest {
	//数组引用
    //Function中的R apply(T t)
    @Test
    public void test4(){
        Function<Integer, String[]> func = new Function<Integer, String[]>() {
            @Override
            public String[] apply(Integer length) {
                return new String[length];
            }
        };

        System.out.println("=====================");

        Function<Integer, String[]> func1 = length -> new String[length];
        String[] arr1 = func1.apply(5);
        System.out.println(Arrays.toString(arr1));

        System.out.println("=====================");

        Function<Integer, String[]> func2 = String[] :: new;
        String[] arr2 = func2.apply(10);
        System.out.println(Arrays.toString(arr2));
    }

}

你可能感兴趣的:(java,java,开发语言)