Lambda表达式
Java Lambda表达式是Java 8中最重要的新特性之一。
是一种常用的函数对象表现形式,它们是一种可传递的匿名函数,可以作为参数传递给方法或存储在变量中,因此可以在需要的时候调用它们。
Lambda表达式的主要目的是简化Java代码,使其更易于阅读和编写。
表达形式
Lambda表达式的语法非常简洁和清晰。它们由参数列表、箭头符号和方法体组成。
- 参数列表指定传递给Lambda表达式的参数
- 箭头符号 “->” 分隔参数列表和方法体
- 方法体则包含Lambda表达式要执行的代码。
例子:
(int a , int b) -> { int c = a + b; return c;}
这是常规的Lambda表达式写法
简写的特殊情况:
-
方法体只包含一行,则可以省略{}和return,代码多于一行,不能省略{}以及最后一行的return
(int a , int b) -> a + b;
-
只有一个参数则可以省略(),同时将类型声明去掉(加了类型声明就必须加上())
a -> System.out.println(a)
-
可以通过上下文推断出参数列表的类型,则无需另外声明参数类型
(a, b) -> a + b;
Lambda1 lambda = (a, b) -> a + b;
interface Lambda1 {
int op(int a, int b);
}
这里通过接口能够推断出参数类型,故使用时无需再声明参数类型
表述练习
1.判断语法正确性
- 1.错误原因是因为两个参数需要加()
- 2.正确,可以通过上下文推断出参数类型,可以不加参数类型
- 3.错误,虽然可以通过上下文推断出参数类型,但是要么都加,要么都不加
- 4.错误,一个参数可以省略(),同时也要去除类型声明,如果声明的参数类型,则需要加上()
函数式接口
可以将参数相同(个数和参数类型相同)以及返回值类型相同的函数统一管理
有且仅有一个抽象方法
1.自定义函数式接口
import java.math.BigInteger;
import java.util.ArrayList;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
static class Student {
String name;
int age;
public Student (){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
//obj1和obj2都是传入一个int参数,返回Boolean值,所以可以使用同一个函数式接口进行定义
Type1 obj1 = (int a) -> (a & 1) == 1;
Type1 obj2 = (int a) -> BigInteger.valueOf(a).isProbablePrime(100);
Type2 obj3 = (int a, int b, int c) -> a + b + c;
//obj4和obj5都是传入两个int参数,返回int值,所以也可以使用同一个函数式接口进行定义
Type3 obj4 = (int a, int b) -> a * b;
Type3 obj5 = (int a, int b) -> a - b;
//Type4 obj6 = () -> new Student();
//Type5 obj7 = () -> new ArrayList<Student>();
//在定义函数式接口时,为了通用性,可以将两者的返回值设置为泛型
Type6<Student> obj6 = () -> new Student();
Type6<ArrayList<Student>> obj7 = () -> new ArrayList<Student>();
//obj8和obj9二者的参数一样,但是返回类型不一样,为了通用性,可以将二者的函数式接口的参数和返回类型都设置为泛型,在定义时传入需要使用的类型包装类即可
Type7<Student, String> obj8 = (Student s) -> s.getName();
Type7<Student, Integer> obj9 = (Student s) -> s.getAge();
}
@FunctionalInterface
interface Type1 {
boolean op(int a);
}
@FunctionalInterface
interface Type2 {
int op(int a, int b, int c);
}
@FunctionalInterface
interface Type3 {
int op(int a, int b);
}
@FunctionalInterface
interface Type4 {
Student op();
}
@FunctionalInterface
interface Type5 {
ArrayList<Student> op();
}
@FunctionalInterface
interface Type6<T> {
T op();
}
@FunctionalInterface
interface Type7<I, O> {
O op(I input);
}
}
其中参数类型也可以进一步省略(可以由函数式接口推断出参数类型)
2.JDK常见函数式接口
上述自定义的函数式接口在JDK中基本上都提供了,一般我们在编程时都是直接使用内置的函数式接口进行简化编程。
比如:Type7 对于Function接口
常见函数式接口
通过名字猜出函数式接口的参数以及返回值类型:
Consumer(消费者):消费,也就是传入之后被消费了,无返回值
IntConsumer:int参数类型
Predicate(条件):判断,返回值为Boolean
Bi:两参
Operator: 参数和返回值类型一样
常见名称含义
应用例子:
1.在外部操作方法执行不同的规则
改变方法的形式,使得后续只需要在外部调用方法时传入不同的筛选规则则可以完成不同的筛选
import java.util.ArrayList;
import java.util.List;
public class Exercise3 {
public static void main(String[] args) {
}
static List<Integer> filter(List<Integer> list) {
List<Integer> result = new ArrayList<Integer>();
for (Integer number : list) {
//筛选条件:判断是否为偶数
//后面希望在不改变该方法的前提下,在外部通过调用该方法,传入不同筛选规则来改变方法的执行结果
if ((number & 1) == 0) {
result.add(number);
}
}
return result;
/*
1. 将筛选条件提取出来:
(number & 1) == 0
2. 该语句只是执行体,也就是方法体,我们需要知道传入什么参数才能执行
(Integer number) -> (number & 1) == 0
3. 通过该lambda表达式找到对应的函数式接口: 传入一个参数,返回Boolean类型 =》Predicate
改写该方法
*/
}
}
改写之后:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Exercise3 {
public static void main(String[] args) {
List<Integer> result = filter(List.of(1, 2, 3, 4, 5, 6), (Integer number) -> (number & 1) == 0);
System.out.println(result);
}
static List<Integer> filter(List<Integer> list, Predicate<Integer> predicate) {
List<Integer> result = new ArrayList<Integer>();
for (Integer number : list) {
//筛选条件:判断是否为偶数
//后面希望在不改变该方法的前提下,在外部通过调用该方法,传入不同筛选规则来改变方法的执行结果
if (predicate.test(number)) {
result.add(number);
}
}
return result;
/*
1. 将筛选条件提取出来:
(number & 1) == 0
2. 该语句只是执行体,也就是方法体,我们需要知道传入什么参数才能执行
(Integer number) -> (number & 1) == 0
3. 通过该lambda表达式找到对应的函数式接口: 传入一个参数,返回Boolean类型 -> Predicate
改写该方法,
4. 方法调用
*/
}
}
解释:
(Integer number) -> (number & 1) == 0)
作为一个函数对象,也就是Predicate
接口的实现:Predicate<Integer> predicate
传入filter
方法中
函数对象中逻辑 (number & 1) == 0
的执行:
- 通过
Predicate
函数接口中的test
方法就可以执行函数对象中的逻辑:predicate.test(number)
后面我们只需要在调用方法时,传入不同的函数对象,就能根据不同的规则来改变执行结果,而不需要修改方法
比如:
现在的筛选条件: (number & 1) == 0)
是筛选偶数
后面只需要简单修改: (number & 1) == 1)
就变为筛选奇数
2.在外部操作方法实现不同的字符串操作
改变方法的形式,使得后续只需要在外部调用方法时传入不同的字符串操作可以完成不同的字符串处理
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class Exercise3 {
public static void main(String[] args) {
List<String> result1 = map(List.of(1, 2, 3, 4, 5), (Integer number) -> String.valueOf(number));
System.out.println(result1);
}
static List<String> map(List<Integer> list, Function<Integer, String> function) {
List<String> result = new ArrayList<>();
for (Integer number : list) {
//转换:将数字转为字符串,但以后可能改变转换规则
result.add(function.apply(number));
}
return result;
/*
1. 将执行的主逻辑提取出来
String.valueOf(number)
2. 补全函数对象
(Integer number) -> String.valueOf(number);
3. 找到对应的函数式接口
一个参数,一个返回值: Function
4. 方法中传入Function的对象,实际调用方法时将Lambda表达式作为函数对象传入方法,从而调用对应接口实现的抽象方法完成逻辑的执行
*/
}
}
解释:
(Integer number) -> String.valueOf(number) + 1
作为一个函数对象,也就是Function
接口的实现:Function<Integer, String> funtion
传入map
方法中
方法中传入Function的对象,实际调用方法时将Lambda表达式作为函数对象传入方法,从而调用Function接口的抽象方法完成逻辑的执行
比如:
现在的字符串处理是:(Integer number) -> String.valueOf(number)
是单纯将提供的数字转成字符串
后面只需要简单修改: (Integer number) -> String.valueOf(number) + 1
就变为先将提供的数字转成字符串,然后在其后面拼接一个字符1
,比如:2 -> "2" + "1" = "21"
3.传入参数无返回值的操作修改
改变方法的形式,使得后续只需要在外部方法时,只需要传入不同的消费规则就能实现对应的操作
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class Exercise3 {
public static void main(String[] args) {
consume(List.of(1, 2, 3, 4, 5), (Integer number) -> System.out.println(number));
}
static void consume(List<Integer> list, Consumer<Integer> consumer) {
for (Integer number : list) {
//消费: 打印,但是后面可能会改变消费规则
consumer.accept(number);
}
}
}
现在的处理是:(Integer number) -> System.out.println(number))
是单纯将提供的数字打印出来
后面只需要简单修改: (Integer number) -> System.out.println(number * 2))
就变为打印提供数字的平方
现在看上去是有些脱裤子放屁,但是后面深入的话,该模式还是有很复杂的用处,请务必好好理解
上述内容是学习过程中摘录的片段,不作为商业用途,如有侵权,联系删除。