1. 本质:
Lambda表达式是java对数学函数表达式的一种体现形式,本质是一个值,在java中主要是体现在对特殊的匿名内部类对象的一种表示,代表匿名内部类的对象
2. 使用前提:
函数式接口:只有一个抽象方法的接口(可以有其他非抽象方法)
可以在接口声明之上, 使用注解: @FunctionalInterface标识验证这个函数式接口
3. 好处:
对匿名内部类对象的格式简化,大幅提升开发效率
注: Lambda表达式代表的匿名内部类的对象,在编译的时候不需要生成对应类的字节码文件,而匿名内部类在编译的时候需要编译生成对应的字节码文件,所以Lambda表达式可以提高效率
1. 格式:
(参数列表) -> {方法体}
2. 详细说明:
(1) (参数列表): 表示要实现的接口中,抽象方法的参数
(2) -> : 箭头运算符,或者称为Lambda运算符,用于分隔前后两部分
(3) {方法体}: 也称为Lambda体,表示重写抽象方法的具体实现
@FunctionalInterface public interface MyFunctionalInterface { public abstract void fun(); } class Test{ public static void main(String[] args) { // 1. 使用匿名内部类实现MyFunctionalInterface接口 new MyFunctionalInterface(){ @Override public void fun() { System.out.println("我是重写的fun,匿名内部类对象方法"); } }.fun(); // 2. 使用lambda表达式实现MyFunctionalInterface接口 // (参数列表)->{方法体} MyFunctionalInterface my = ()->{ System.out.println("lambda表达式重写的fun"); }; my.fun(); } }
1. 有且只有一个参数,可以省略小括号
x -> {int result = x * x; System.out.println(x + “的平方为:” + result);}
2. Lambda体只有一句,且没有返回值,可以省略大括号
x -> System.out.println(x * x);
3. Lambda体只有一句,且有返回值,则return和大括号可以一起省略
(x, y) -> {return x + y;} 等价于 (x, y) -> x + y
注意:要么一起省略,要么都不省略
@FunctionalInterface public interface MyFunctionalInterface { public abstract void fun(); } @FunctionalInterface interface Inter2{ public abstract void print(int n); } @FunctionalInterface interface InterOther{ public abstract boolean equal(double d, double d1); } class Test{ public static void main(String[] args) { // 1. 使用匿名内部类实现MyFunctionalInterface接口 new MyFunctionalInterface(){ @Override public void fun() { System.out.println("我是重写的fun,匿名内部类对象方法"); } }.fun(); // 2. 使用lambda表达式实现MyFunctionalInterface接口 // (参数列表)->{方法体} MyFunctionalInterface my = ()->{ System.out.println("lambda表达式重写的fun"); }; my.fun(); // 3. 使用lambda表达式实现参数列表只有一个的抽象方法 Inter2 i2 = (x) -> { for(int i = 1; i <= x; i++){ System.out.println(i); } }; i2.print(5); // 特殊情况1: 如果使用lambda表达式作为一个函数式接口的实现类对象, 需要实现的唯一抽象方法 // 参数列表只有一个参数, 那么lambda表达式的小括号可以省略 Inter2 i3 = n -> { System.out.println(n); }; i3.print(8); // 特殊情况2: 如果lambda表达式实现的抽象方法对应的方法体逻辑只有一句,并且没有返回值结果 // 那么大括号可以省略 Inter2 i4 = x -> System.out.println("x的值为:" + x); // 4. 使用lambda变道时实现参数列表有多个,有返回值抽象方法 // 特殊情况3: 如果lambda表达式实现的抽象方法有返回值结果,并且逻辑只有一句,这一句逻辑正好对应方法 // 的返回值, 那么return和大括号可以同时省略 InterOther other = (x,y)->{return x == y;}; System.out.println(other.equal(5.6,5.7));// false InterOther other3 = (x,y) -> x==y; System.out.println(other3.equal(4.1,4.1));// true } }
1、定义:如果在接口中,只有一个抽象方法,那么这个接口就是函数式接口,比如:jdk中熟悉的函数式接口 :Runnable
2、格式说明:
使用注解来检查当前接口是否是一个函数式接口
@FunctionalInterface
如果不是函数式接口,则编译报错
3、理解:
(1) 函数:想表达的是一个方法的内容,由于方法不在任何类中,所以称为函数
(2) 函数式接口:其实想表达的就是一个抽象函数的声明
4、作用:
使用函数式接口表达函数的声明;使用函数式接口的实现类对象表达函数的实现
5、使用原因:
Java中不支持将函数作为一个数据,也就不能将这个函数进行各种传递,也就不能作为对象的成员变量存在
只能在方法外加一层接口的声明,将来可以传递方法所在接口的实现类对象,来间接的传递方法实现内容
举例 : 客户要求定义出一个方法功能, 对两个整数进行任意操作
1) 两数求和
2) 两数求差
3) 两数乘积 * 2
4) ... 无数需求, 都需要在同一个方法中完成
public int useTwoInt(int x, int y, 对于x和y两数的操作思想,这个思想可以是一个抽象方法){
}
1、说明:
Java8中提供了一些常用的函数式接口,在使用类似功能的时候,不需要额外定义接口,直接使用jdk中提供的即可
2、罗列:
Consumer<T>:消费型接口
void accept(T t): 需要传进进来参数,进行消费,没有返回值。【有进无出】
3、 Supplier<T>:供给型接口
T get(): 不需要传递参数,返回创建一个数据的接口 【无进有出】
4、 Function<T, R>:函数型接口
R apply(T t): 需要传递一个参数,把传递进来的参数进行一些操作,然后再返回一个参数【有进有出】
5、 Predicate<T>:断言型接口
boolean test(T t): 需要参数,返回一个boolean类型的值【有进有出,出来的结果一定是boolean类型】
1、Consumer<T> : 消费型接口
2、抽象方法:void accept(T t) 消费使用掉任意的一个数据
3、作用:
定义一个方法,这个方法需要传递进来数据,还需要传递进来处理这个数据的方式,并且还不需要返回值类型,就可以使用Consumer接口来当做数据处理方式的传递。
有了Consumer接口接口以后,不仅仅可以传递数据,还可以传递消费处理数据的方式。
案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来
1) 客户1 : 花了500元, 买了一把大宝剑
2) 客户2 : 花了500元, 买了一堆化妆品
3) 客户3 : 花了500元, 买了一双球鞋
4) .... 还有无限的客户有不同种消费方式
import java.util.function.Consumer; public class ConsumerInterfaceUse { public static void main(String[] args) { Consumer<Double> con1 = x -> System.out.println("花了" + x + "元买了一把大宝剑"); // 1)客户1 : 花了500元, 买了一把大宝剑 testConsumer(500,con1); // 2)客户2 : 花了500元, 买了一堆化妆品 Consumer<Double> con2 = x -> { if(x >= 500){ System.out.println("消费超出了500元,无法购物"); }else{ System.out.println("花了" + x + "元买了一堆化妆品"); } }; testConsumer(500,con2); } /*案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来 1)客户1 : 花了500元, 买了一把大宝剑 2)客户2 : 花了500元, 买了一堆化妆品 3)客户3 : 花了500元, 买了一双球鞋 4).... 还有无限的客户有不同种消费方式 参数列表分析: 1. 参数1表示客户需要花费的预期金额 2. 参数2需要对于double类型的参数1,消费(使用)方式,因此提供Consumer<Double> con, 就是为了传递 accept(T t): 对于double类型数据进行消费(使用) */ public static void testConsumer(double money, Consumer<Double> con){ con.accept(money); } }
1、Supplier<T>:供给型接口
2、抽象方法:T get() 提供返回任意类型数据的规则
3、作用:
当一个方法需要去生产一些数据,并且生产数据的方式不确定,就可以在方法的形参列表位置传递一个 Supplier接口,将来让调用该方法的调用者把如何生产数据的方式传递进来。有了供给型接口之后,不仅仅可以传递生产数据的数量等信息,也可以传递生产数据的方式。
案例 : 定义出一个方法功能, 能给客户返回出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律, 由客户决定, 方法主要给客户返回一个符合客户要求的容器
1) 客户1 : 5个数据, 都是30-80之间的随机数
2) 客户2 : 8个数据, 1-100之间的随机偶数
3) ...
import java.util.ArrayList; import java.util.Random; import java.util.function.Supplier; public class SupplierInterfaceUse { public static void main(String[] args) { // 1)客户1 : 5个数据, 都是30-80之间的随机数 // (0-50) + 30 -->30--80 Supplier<Integer> sup = ()->new Random().nextInt(51) + 30; ArrayList<Integer> list = getArrayList(5,sup); System.out.println(list); // 2)客户2 : 8个数据, 1-100之间的随机偶数 Supplier<Integer> sup2 = ()->{ Random ran = new Random(); int number = ran.nextInt(100)+1; while(number % 2 != 0){ number = ran.nextInt(100)+1; } return number; }; list = getArrayList(8,sup2); System.out.println(list); } /* 案例 : 定义出一个方法功能, 能给客户返回出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律, 由客户决定, 方法主要给客户返回一个符合客户要求的容器 1)客户1 : 5个数据, 都是30-80之间的随机数 2)客户2 : 8个数据, 1-100之间的随机偶数 3)... 分析参数列表: 1. 需要客户提供目标存储的数据个数 2. 需要客户提供容器中存储的数据对应的规则,需要Integer类型数据, 因此提供一个Supplier<Integer>, 实际上提供的就是 Integer get() */ public static ArrayList<Integer> getArrayList(int count, Supplier<Integer> sup){ ArrayList<Integer> list = new ArrayList<>(); if(count > 0){// 存储数据到list集合 for(int i = 1; i <= count; i++){ list.add(sup.get()); } }else{// 直接返回list即可 return list; } return list; } }