一、lambda表达式
1、概念
- Lambda表达式时一种特殊的匿名内部类,语法更加简洁。
- Lambda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递
这里的匿名内部类的理解,我们可以在下述情况中来帮助大家了解。
我们都知道,接口是不能直接进行new的,即接口没有实例对象。那么我们之前想要new对象是不是还需要写该接口的一个实现类,然后再去new这个接口实现类的实例对象?
在某些情况下,我们想要的也许只是接口中的一个抽象方法,其他的内容对我们来说都不重要,那么我们可以直接用接口的匿名内部类的方法来“new 这个接口”。
对于这个Runnable接口,我们可以直接new他的匿名内部类。
package demo04;
/**
* @作者:刘壬杉
* @创建时间 2022/7/20 9:28
**/
public class Test {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
//方法体
}
};
}
}
对于 “=” 号后面的这一部分,都是他的匿名内部类。Lambda表达式就是一种特殊的匿名内部类。他的语法更加简洁。
注意,想要将匿名内部类改写为Lambda表达式的前提是,该接口必须为函数式接口(接口中只能有一个抽象方法的接口称为函数式接口)。
2、语法
<函数式接口> <变量名> = (参数1,参数2) ->{方法体};
注意:
- (参数1,参数2): 抽象方法的参数
- ->: 分隔符
- { }:表示抽象方法的实现
我们还是用Runnable接口来举例子。Runnable接口就是一个函数式接口
左边的代码就是Lambda表达式。他完全等价于右边匿名内部类的代码。
再强调一遍,想要使用Lambda表达式,该接口必须是函数式接口!
3、详细介绍Lambda表达式
- Lambda表达式引入了新的操作符:->(箭头操作符),该操作符将表达式分成了两部分:
- 左侧:(参数1,参数2,...)表示参数列表(接口中抽象方法需要的参数列表)
- 右侧:"{}"的内部是方法体
- 注意事项:
- 参数列表的数据类型会自动推断。我们在左侧括号内写参数列表时,不需要写参数的参数类型,直接写参数名即可。
- 如果参数列表为空,我们只需要写一个()即可(例如上面的Runnable接口中的run方法就的参数列表就为空)
- 如果参数列表只有一个,那么()可以省略,只需要写参数的名称即可
- 如果执行的语句只有一句,且无返回值,那么“{ }”可以省略不写;如果执行语句有返回值且只有一句,那么在省略“{ }”的同时,也要省略掉“return”关键字
- Lambda不会生成一个单独的内部类文件
二、函数式接口
1、概念
- 如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用lambda表达式,Lambda表达式会被匹配到这个抽象方法上
- @FunctionalInterface 注解会检测接口是否符合函数式接口
2、内置函数式接口
举个例子,我们想要用Lambda表达式来实现数组求和,就需要有一个函数式接口,那么我们就必须先定义一个函数式接口
@FunctionalInterface
interface Operater{
//求数组的和
public abstract void getSum(int[] arr);
}
然后再实现我们的求和功能
public class Test {
public static void main(String[] args) {
Operater o=arr -> {
int sum=0;
for(int n:arr){
sum+=n;
}
System.out.println("数组的和为:"+sum);
};
fun(o);
}
public static void fun(Operater operater){
int[] arr={2,3,4,5,6,7,11};
operater.getSum(arr);
}
}
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。这些接口都在java.util.function包下保存
这四种函数式接口我们都举一个例子来方便大家理解:
还是用我们的数组求和的代码。我们用Consumer,Supplier,Function这三种函数式接口对其进行改写,最后Predicate我们单独举个例子。
自定义函数式接口实现数组求和的代码:
package FunctionalInterfaceTest;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:13
* 求数组的和 ---- 传统形式
**/
public class TraditionalMethod {
public static void main(String[] args) {
Num num = (arr)->{
int sum = 0;
for (Integer integer : arr) {
sum+=integer;
}
System.out.println(sum);
};
fun(num);
}
public static void fun(Num num){
Integer[] arr = {1,2,3,4,5,6,7,8,9};
num.getNum(arr);
}
}
interface Num{
public void getNum(Integer[] arr);
}
2.1 Consumer<T> 消费型函数式接口 有参无返回值
T:参数类型的泛型
该函数式接口的抽象方法为 accept( 参数名 );
package FunctionalInterfaceTest;
import java.util.function.Consumer;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:19
* 求数组的和
* 有参无返 消费型
**/
public class ConsumerMethod {
public static void main(String[] args) {
Consumer<Integer[]> consumer = (arr)->{
int sum = 0;
for (Integer integer : arr) {
sum+=integer;
}
System.out.println(sum);
};
Integer[] integers = {1,2,3,4,5,6,7,8,9};
fun(consumer,integers);
}
public static void fun(Consumer<Integer[]> consumer,Integer[] arr){
consumer.accept(arr);
}
}
2.2 Supplier<T> 供给型函数式接口 无参有返回值
T:返回结果的泛型
该函数式接口的抽象方法为 T get( );
package FunctionalInterfaceTest;
import java.util.function.Supplier;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:22
* 求数组的和
* 无参有返回值 供给型
**/
public class SupplierMethod {
public static void main(String[] args) {
Supplier<Integer> supplier = ()->{
Integer[] integers = {1,2,3,4,5,6,7,8,9};
Integer sum = 0;
for (Integer integer : integers) {
sum+=integer;
}
return sum;
};
fun(supplier);
}
public static void fun(Supplier<Integer> supplier){
Integer integer = supplier.get();
System.out.println(integer);
}
}
2.3 Function<T,R> 函数型函数式接口 有参有返回值
T:参数类型的泛型
R:函数返回结果的泛型
其抽象方法为: R apply( 参数名 )
package FunctionalInterfaceTest;
import java.util.function.Function;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:28
* 求数组的和/传入一个字符串,返回字符串的长度
* 有参 有返回值 函数型
**/
public class FunctionMethod {
public static void main(String[] args) {
//数组求和
Function<Integer[],Integer> function = (arr)->{
Integer sum = 0;
for (Integer integer : arr) {
sum+=integer;
}
return sum;
};
Integer[] arr = {1,2,3,4,5,6,7,8,9};
fun(function,arr);
//计算字符串的长度
getLength(str->str.length(),"hello lambda");
}
//数组求和方法
public static void fun(Function<Integer[],Integer> function,Integer[] arr){
Integer apply = function.apply(arr);
System.out.println(apply);
}
public static void getLength(Function<String,Integer> function,String str){
Integer apply = function.apply(str);
System.out.println("该字符串的长度为:"+apply);
}
}
2.4 Predicated<T> 断言型函数式接口 有参,返回布尔值
T:参数类型的泛型
抽象方法: boolean test(参数名)
例子:判断传入字符串的长度是否大于10
package FunctionalInterfaceTest;
import java.util.function.Predicate;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:31
* 判断字符串的长度是否大于10
**/
public class PredicatedMethod {
public static void main(String[] args) {
NameIsBigThanThree(name->name.length()>10?true:false,"达拉崩吧版的本地不多秘鲁翁");
}
public static void NameIsBigThanThree(Predicate<String> predicate,String name){
boolean test = predicate.test(name);
System.out.println("该字符串长度大于10是"+test);
}
}
三、方法引用
1、lambda表达式的冗余
如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引 用”过去就好了:---方法引用。
public class Test01 {
public static void main(String[] args) {
Consumer<Integer[]> c=Test01::sum;
fun(c);
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] arr={1,2,3,4,5};
consumer.accept(arr);
}
public static void sum(Integer[] arr){
int sum=0;
for (int a:arr){
sum+=a;
}
System.out.println("数组的和为:"+sum);
}
}
请注意其中的双冒号 :: 写法,这被称为“方法引用”,是一种新的语法。
2、 什么是方法引用
方法引用的分类:
四种引用方法具体例子:
2.1 静态方法引用
类名::静态方法 代替--> (参数)->类名.静态方法(参数)
对象类型数组或集合的排序:
package referencedByMethodsTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 19:42
* 排序
**/
public class StaticMethodReferencedTest {
public static void main(String[] args) {
//Person数组
Person[] ps ={new Person("张三",18),new Person("李四",25),new Person("王五",20),new Person("张三",19)};
Arrays.sort(ps,Person::compareTo);
for (Person p : ps) {
System.out.println(p+"```````数组````````");
}
//Person集合
List<Person> list = new ArrayList<>();
list.add(new Person("张三",51));
list.add(new Person("李四",35));
list.add(new Person("王五",27));
list.add(new Person("赵六",60));
list.sort(Person::compareTo);
for (Person person : list) {
System.out.println(person+"========集合======");
}
}
}
class Person{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
//自定义排序方法
public static int compareTo(Person o1,Person o2){
return o2.age - o1.age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.2 实例方法引用
对象::实例方法 代替--> (参数)->对象.实例方法(参数)
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
例子和静态方法引用里面的例子相同,注意二者的区别
package referencedByMethodsTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @作者:刘壬杉
* @创建时间 2022/7/19 20:24
**/
public class InstanceMethodReferencedTest {
public static void main(String[] args) {
//people数组排序
People[] people = {new People("张三",15),new People("李四",27),new People("王五",19),new People("赵六",35)};
People people1 = new People();
//实例方法引用
Arrays.sort(people,people1::compareTo);
for (People people2 : people) {
System.out.println(people2+"------数组排序-----");
}
//people集合排序
List<People> list = new ArrayList<>();
list.add(new People("张三",55));
list.add(new People("李四",37));
list.add(new People("王五",68));
list.add(new People("赵六",25));
list.sort(people1::compareTo);
for (People people2 : list) {
System.out.println(people2+"++++++集合排序++++++");
}
}
}
class People {
private String name;
private Integer age;
public People() {
}
public People(String name, Integer age) {
this.name = name;
this.age = age;
}
//自定义排序方法
public int compareTo(People o1, People o2){
return o2.age - o1.age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.3 对象方法引用
类名::实例方法 代替--> (参数1,参数2)->参数1.实例方法(参数2)
package test.methodReference;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* @作者:刘壬杉
* @创建时间 2022/7/20 18:59
* 对象引用方法 类::实例方法 替换 (参数1,参数2)->参数1.实例方法(参数2)
**/
public class ObjectReferenceMethod {
public static void main(String[] args) {
//比较两个字符串的值是否相等
//Lambda表达式方法
BiFunction<String,String,Boolean> function = (t1,t2)->t1.equals(t2);
Boolean apply = function.apply("hello", "hello");
System.out.println(apply);
//对象引用方法
BiFunction<String,String,Boolean> function1 = String::equals;
Boolean apply1 = function1.apply("hello", "hello");
System.out.println(apply1);
//计算字符串的长度
//lambda表达式
Function<String,Integer> function2 = (str)->str.length();
Integer hello_world = function2.apply("hello world");
System.out.println(hello_world);
//对象引用方法
Function<String,Integer> function3 = String::length;
Integer hello_world1 = function3.apply("hello world");
System.out.println(hello_world1);
}
}
2.4 构造方法引用
类名::new 代替--> (参数)->new 类名(参数)
package test.methodReference;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* @作者:刘壬杉
* @创建时间 2022/7/20 19:09
* 构造方法引用 类名::new 替换 (参数)->new 类名(参数)
**/
public class ConstructorReferenceMethod {
public static void main(String[] args) {
//(1) new String
//Lambda表达式
Function<String,String> function = (str)->new String(str);
String hello_world = function.apply("hello world");
System.out.println(hello_world);
//构造方法引用
Function<String,String> function1 = String::new;
String hello_world1 = function1.apply("hello world");
System.out.println(hello_world1);
//(2) new Person
//lambda表达式
BiFunction<String,Integer,Person> biFunction = (name,age)->new Person(name,age);
Person p = biFunction.apply("张三", 18);
System.out.println(p);
//构造方法引用
BiFunction<String,Integer,Person> biFunction1 = Person::new;
System.out.println(biFunction1.apply("张三",18));
}
}
class Person{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}