day19---JDK8新特性
1. Lambda表达式
- Lambda本质:
就是一个对象, 指定函数式接口的实现类对象
- Lambda表达式使用前提:
接口必须是函数式接口 : 接口中只有一个抽象方法
使用注解 @FunctionalInterface 验证和标识接口就是一个函数式接口
- Lambda优势:
大大简化匿名内部类对象实现过程, 从而提高代码的运行效率
- Lambda表达式语法结构:
(参数列表)->{方法体}; // 表达式整体就表示一个函数式接口的实现类对象
- 解释Lambda表达式语法:
因为Lambda表达式只能用于函数式接口的实现, 接口中只有一个抽象方法, 因此Lambda表达式很明确其需要重写方法是哪个
(参数列表) : 唯一的抽象方法中的参数列表
-> : 称为Lambda运算符, 也称为箭头运算符
{方法体} : 表示唯一抽象方法实现过程
- Lambda表达式使用的注意事项:
a : 参数列表: Lambda表达式参数列表中, 只罗列出参数个数和变量名,不需要写参数的数据类型
1) Lambda表达式重写方法, 没有参数, 小括号就是空的 ()
2) Lambda表达式重写方法, 只有一个参数, 小括号可以省略
3) Lambda表达式重写方法, 有多个参数, 按照方法的参数列表, 列出参数名称即可
b : 方法体:
1) Lambda表达式重写方法体, 只有一句表达式, 那么大括号可以省略
2) Lambda表达式重写方法体, 只有一句代码逻辑, 并且这一句就是方法返回值结果, 那么大括号和return关键字可以同时省掉
代码
package com.ujiuye.lambda; public class Demo01_Lambda表达式语法结构 { public static void main(String[] args) { // 1.匿名内部类对象实现接口Inter1 new Inter1() { @Override public void fun() { System.out.println("匿名内部类对象实现方法fun"); } }.fun(); // 2. Lambda表达式实现接口Inter1 // (参数列表)->{方法体};// 接口Inter1的实现类对象 Inter1 t = ()->{ System.out.println("Lambda表达式实现方法fun"); }; t.fun(); } } @FunctionalInterface interface Inter1{ public abstract void fun(); } |
package com.ujiuye.lambda; public class Demo02_Lambda表达式使用注意事项 { public static void main(String[] args) { // 1. 匿名内部类对象实现Inter2 new Inter2() { @Override public void print(String s) { System.out.println(s + "----"); } }.print("hello"); // 2. Lambda表达式实现Inter2 Inter2 i1 = (a)->{ System.out.println(a + "++++"); }; i1.print("world"); // 3. Lambda表达式实现方法只有一个参数, 参数小括号可以省略 Inter2 i2 = y -> {System.out.println(y + "--啦啦啦啦");}; i2.print("123"); // 4. Lambda表达式实现方法有多个参数 Inter3 i3 = (a,b)->{ System.out.println(a + b); }; i3.getSum(3, 5);// 8 // 5. Lambda表达式实现方法体只有一句代码逻辑, 大括号可以省略 Inter2 i4 = y->System.out.println(y); i4.print("省略大括号"); // 6. Lambda表达式实现接口Inter4 Inter4 i5 = (x,y)->{ boolean boo = x == y; System.out.println(boo); return boo; }; i5.equal(3, 4);// false // 7. Lambda表达式实现接口Inter4, 方法带有返回值类型, 大括号省掉 Inter4 i6 = (a,b)->a==b; System.out.println(i6.equal(3, 3));// true } } @FunctionalInterface interface Inter2{ public abstract void print(String s); } @FunctionalInterface interface Inter3{ public abstract void getSum(int a, int b); } @FunctionalInterface interface Inter4{ public abstract boolean equal(int a, int b); } |
2. 函数式接口
2.1 函数式接口的概述
- 函数式接口介绍:
函数式接口中只有一个抽象方法, 使用注解@FunctionalInterface 验证和标识接口就是一个函数式接口 , Lambda表达式只能实现函数式接口
- 函数式接口存在意义:
Java代码定义方法功能, 根据方法实现情况, 需要参数列表, Java方法中的参数列表 数据类型 变量名, 数据类型 变量名..., 方法参数都必须带有指定数据类型 , 但是有一些方法实现过程中, 不仅仅需要固定类型参数, 也需要对于数据的处理方式(处理思想)
举例 : 定义出一个方法功能, 可以操作两个int类型整数, 实际操作过程按照客户实际需要进行
客户1 : 做两个整数求和
客户2 : 做两个整数求差
客户3 : 做两个整数求乘
...
分析 : 目前方法功能, 不仅仅需要两个整数int, 同时需要提供对各两个数据操作的想法,思想
数据处理思想,方式, 其实就是对应Java中的一个方法功能,但是Java中不支持将方法作为参数传递, 于是将这个方法封装在一个接口中, 将接口作为方法参数传递, 相当于在传递接口中方法, 传递对于数据的处理思想, 因此函数式接口是为了封装某一个方法而存在
2.2 JDK提供常用内置函数式接口
JDK8版本开始, 提供了4个常用函数式接口, 在不同的数据思想操作层面上, 可以直接使用对应的函数式接口, 而不需要自定义, 函数式接口都来自于 java.util.function包
- 消费型接口:
Consumer<T>
void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式
- 供给型接口:
Supplier<T>
T get() : get方法就是可以返回一个T类型结果, 不需要任何参数
- 函数式接口:
Function<T,R>
R apply(T t) : apply方法通过参数t, 获取到结果R
- 断言型接口:
Predicate<T>
boolean test(T t) : test 方法提供参数t, 验证这个参数是否符合某种规则
2.3 消费型接口
Consumer<T>
void accept(T t) : 提供T类型参数, 在方法中对于参数t进行消费, 消费指对于数据t的处理方式; 如果定义一个方法功能, 不仅仅需要参数t, 还需要对于参数t类型数据的消费方式, Consumer可以作为方法参数进行传递
案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来
1) 客户1 : 花了500元, 买了一把大宝剑
2) 客户2 : 花了500元, 买了一堆化妆品
3) 客户3 : 花了500元, 买了一双球鞋
4) .... 还有无限的客户有不同种消费方式
代码
package com.ujiuye.function; import java.util.function.Consumer; public class Demo01_消费型接口 { public static void main(String[] args) { // 客户1 : 花了500元, 买了一把大宝剑 int money = 500; // 调用testConsumer方法, 第二个参数需要一个Consumer接口的实现类 // 使用Lambda表达式作为Consumer接口的实现类对象 Consumer<Integer> con = x->System.out.println("花了"+x+"元,买了一把大宝剑"); testConsumer(money,con); // 2.客户2 : 花了500元, 买了一堆化妆品 Consumer<Integer> con1 = x->{ if(x >= 500) { System.out.println("不能消费,超出预算"); }else { System.out.println("花了"+x+"元,买了一堆化妆品"); } }; testConsumer(400,con1); } /*案例 : 定义出一个方法功能, 客户预计消费500元现金, 每一个客户对于500元的消费都不同, 将客户的消费方式实现出来 1)客户1 : 花了500元, 买了一把大宝剑 2)客户2 : 花了500元, 买了一堆化妆品 3)客户3 : 花了500元, 买了一双球鞋 4).... 还有无限的客户有不同种消费方式*/ /* * 分析 : * 1) 设定出第一个参数, 表示客户消费的金额 * 2) 设定出第二个参数, 需要 给出客户对于金额消费方式, 提供Consumer<T>, T泛型表示需要消费的数据 * * */ public static void testConsumer(int money,Consumer<Integer> con) { con.accept(money); } } |
2.4 供给型接口
Supplier<T>
T get() : get方法就是可以返回一个T类型结果, 不需要任何参数 ; 如果定义一个方法功能时, 不需要参数, 只需要获取到某一个类型的数据, 而获取方式多种多样, 可以将Supplier类型作为方法参数传递, 实际传递的是获取某类型数据的思想
案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律, 由客户决定, 方法主要给客户返回一个符合客户要求的容器
1) 客户1 : 5个数据, 都是30-80之间的随机数
2) 客户2 : 8个数据, 1-100之间的随机偶数
3) ...
代码
package com.ujiuye.function; import java.util.ArrayList; import java.util.Random; import java.util.function.Supplier; public class Demo02_供给型接口 { public static void main(String[] args) { // 1) 客户1 : 5个数据, 都是30-80之间的随机数 Supplier<Integer> sup = ()->new Random().nextInt(51)+30; System.out.println(testSupplier(5,sup));
// 2) 客户2 : 8个数据, 1-100之间的随机偶数 Supplier<Integer> sup1 = ()->{ Random ran = new Random(); while(true) { int number = ran.nextInt(100)+1; if(number % 2 == 0) {// 偶数 return number; } } }; System.out.println(testSupplier(8,sup1)); }
/*案例 : 定义出一个方法功能, 能给客户创建出一个ArrayList<Integer>类型容器, 容器中装几个数据由客户决定, 容器中承装的数据有什么规律是什么, 由客户决定, 方法主要给客户返回一个符合客户要求的容器 1)客户1 : 5个数据, 都是30-80之间的随机数 2)客户2 : 8个数据, 1-100之间的随机偶数 3)...*/
/* * 分析 : * 1. 设计出第一个参数, int n, n就表示容器中装数据个数 * 2.设计出第二个参数, Supplier<Integer> * Integer get() * */
public static ArrayList<Integer> testSupplier(int n,Supplier<Integer> sup){ ArrayList<Integer> list = new ArrayList<>(); for(int i = 1; i <= n; i++) { // 需要获取一个Integer类型数据 list.add(sup.get()); } return list; } } |
2.5 函数型接口
Function<T,R>
R apply(T t) : 如果定义一个方法功能时, 目前有参数T t, 需要根据t获取到另外一个类型结果R, 那么Function可以作为方法参数传递
Function andThen(Function fun1) :
Function<T,R> fun
Function<R,W> fun1
fun.andThen(fun1) : 需求目前具有T数据, 目标数据W, 可是转换过程中, 直接将T类型转换成W比较困难, 于是具有T,先转换成R类型, 再由R转换成W类型, andThen方法功能, 将方法调用者fun的结果R, 作为参数fun1的第一个泛型尽量连续的转换计算
案例1 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定
1) 客户1 : y为x的2倍
2) 客户2 : y与x相等
3) 客户3 : y为x的平方
4) ...
案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定
5) 客户4 : x为”6” , 计算出x转换成整数后, 2倍结果
6) 客户5 : x为”-2”, 计算出x转换成整数后, +1的结果
...
代码
package com.ujiuye.function; import java.util.function.Function; public class Demo03_函数型接口 { public static void main(String[] args) { // 客户1 : y为x的2倍 Function<Integer,Integer> fun = x->x*2; System.out.println(testFunction(5,fun));// 10
// 客户2 : y与x相等 Function<Integer,Integer> fun1 = x->x; System.out.println(testFunction(99,fun1));// 99
// 客户3 : y为x的平方 Function<Integer,Integer> fun2 = x->x*x; System.out.println(testFunction(3,fun2));// 9
// 客户4 : x为”6” , 计算出x转换成整数后, 2倍结果 Function<String,Integer> fun4 = x->Integer.parseInt(x); Function<Integer,Integer> fun5 = x->x*2; System.out.println(testFunction1("6",fun4,fun5));// 12
// 客户5 : x为”-2”, 计算出x转换成整数后, +1的结果 Function<Integer,Integer> fun6 = x->x+1; System.out.println(testFunction1("-2",fun4,fun6));// -1 }
/*案例 : 定义一个方法功能, 根据整数x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定 1)客户1 : y为x的2倍 2)客户2 : y与x相等 3)客户3 : y为x的平方 4)...*/
/* * 分析 : * 1. 设计出一个参数, 就表示目前拥有整数x * 2.设计出第二个参数, Function<Integer,Integer> * Integer apply(Integer) * */
public static int testFunction(int x, Function<Integer,Integer> fun) { return fun.apply(x); }
/*案例2 : 定义一个方法功能, 根据字符串x,计算出对应整数y, x数据由客户给出, y数据的计算方式根据客户要求决定 5)客户4 : x为”6” , 计算出x转换成整数后, 2倍结果 6)客户5 : x为”-2”, 计算出x转换成整数后, +1的结果 ...*/
public static int testFunction1(String x,Function<String,Integer> fun, Function<Integer,Integer> fun1) { return fun.andThen(fun1).apply(x); } } |
2.6 断言型接口
Predicate<T>
boolean test(T t) : 如果现在具有类型数据T t, 验证这个t是否规则, 可以将Predicate作为方法参数传递
有两个默认方法功能:
Predicate and(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, 做&&判断
Predicate or(Predicate pre) : 表示将方法调用者需要规则与方法参数pre的规则, 做||判断
案例1 :
定义出一个方法, 需要客户提供一个容器ArrayList<Integer>, 通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户
1) 客户1 : 要求容器中的所有数, 都能被2整除
2) 客户2 : 要求所有的数据都不大于100
3) 客户3 : 要求所有数据都小于100, 并且都是奇数
4) 客户4 : 要求所有数据或者小于100, 或者是偶数
5) ...
代码
package com.ujiuye.function; import java.util.ArrayList; import java.util.function.Predicate; public class Demo04_断言型接口 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(12); list.add(15); list.add(120); list.add(111); // 1. 客户1 : 要求容器中的所有数, 都能被2整除 Predicate<Integer> pre = x->x % 2 == 0; System.out.println(testPredicate(list,pre)); // 2. 客户2 : 要求所有的数据都不大于100 Predicate<Integer> pre1 = x->x <= 100; System.out.println(testPredicate(list,pre1)); // 3.客户3 : 要求所有数据都小于100, 并且都是奇数 // Predicate<Integer> pre2 = x->x < 100 && x % 2 != 0; Predicate<Integer> pre3 = x->x < 100; Predicate<Integer> pre4 = x->x % 2 != 0; System.out.println(testPredicate(list,pre3.and(pre4))); // 4.客户4 : 要求所有数据或者小于100, 或者是偶数 System.out.println(testPredicate(list,pre3.or(pre))); } /*案例 : 定义出一个方法, 需要客户提供一个容器ArrayList<Integer>, 通过客户的意愿, 将容器中符合条件的数据筛选出来, 将筛选出的数据放置在新容器中返回给客户 1)客户1 : 要求容器中的所有数, 都能被2整除 2)客户2 : 要求所有的数据都不大于100 3)客户3 : 要求所有数据都小于100, 并且都是奇数 4)客户4 : 要求所有数据或者小于100, 或者是偶数 ...*/ /* * 分析 : * 1. 设计出第一个参数, ArrayList<Integer>, 就表示客户提供原始集合数据 * 2. 设计出第二个参数, Predicate<T> * boolean test(T t) * * */ public static ArrayList<Integer> testPredicate(ArrayList<Integer> list,Predicate<Integer> pre){ ArrayList<Integer> newList = new ArrayList<>(); for(Integer i : list) { if(pre.test(i)) {// 符合规则 newList.add(i); } } return newList; } } |
2.7 方法引用
Lambda表达式 : 作为一个函数式接口实现类对象存在
如果使用Lambda表达式实现一个函数式接口, Lambda体(方法体) 已经被某个类型中某个方法实现过了, 那么可以直接引用这个已经实现的方法作为Lambda表达式实现过程
(Lambda表达式实现一个接口时, A方法已经将需要的实现过程完成好了, 不需要再次将重复的代码在Lambda体中再写一遍, 只需要调用A方法即可)
注意 : Lambda表达式参数列表和方法体实现上都需要与A方法保持一致
方法引用:
1) 函数式接口名 变量 = 类对象 :: 方法名;
2) 函数式接口名 变量 = 类名 :: 方法名;
代码
package com.ujiuye.function; public class Demo05_方法引用 { public static void main(String[] args) { // 1. 使用Lambda表达式实现函数式接口My My my = s->System.out.println(s); my.print("hello"); // 2. 使用方法引用实现函数式接口 My my1 = System.out :: println; my1.print("123-------"); // 3. 使用方法引用实现函数式接口 // My my2 = s->System.out.println(Integer.parseInt(s)+1); My my2 = new Print1() :: print; my2.print("115");// 116 // 4. 使用方法引用实现函数式接口 My my3 = Print2 :: print2; my3.print("10");// 15 } } class Print2{ public static void print2(String s) { System.out.println(Integer.parseInt(s)+5); } } class Print1{ public void print(String s) { System.out.println(Integer.parseInt(s)+1); } } @FunctionalInterface interface My{ public abstract void print(String s); } |
3. StreamAPI
- 在JDK8版本, Collection单列集合顶层父接口中, 添加一个方法功能stream()
1) stream() : 表示获取到当前集合的流资源, 返回值类型Stream接口, 证明stream方法返回Stream的一个实现类对象
2) Stream<T>接口 : 表示流资源, 主要是可以对于单列集合进行操作,访问,筛选相关操作, Stream中的泛型数据就是当前操作集合中的数据类型
- Stream中常用方法:
1) filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用
2) forEach(Consumer<T> con) : 功能遍历Stream中的操作的集合中数据, 参数需要消费型接口, 一边遍历还可以对遍历出元素进行消费性操作
3) count() : 表示获取到流资源中操作的元素个数, 返回值类型long
4) skip(long n) : 表示跳过流资源中的前n个数据, 之后的数据继续进行操作, 返回值类型Stream<T> , 因此方法调用完毕还可以链式调用
5) limit(long n) : 表示保留流资源中前n个数据,之后不操作,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用
6) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用
案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名
1) 将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历
2) 跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历
3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历
代码
package com.ujiuye.stream; import java.util.ArrayList; public class StreamDemo { public static void main(String[] args) { // 案例 : 定义出一个ArrayList<String>, 集合中存储人物的姓名 //1. 定义出一个带有人物名字集合 ArrayList<String> list = new ArrayList<>(); list.add("张无忌"); list.add("张6"); list.add("赵敏"); list.add("周芷若"); list.add("小昭"); list.add("张三丰"); list.add("张成"); // 1)将集合中所有姓张的, 名字有3个字的人名获取到, 并遍历 // filter(Predicate<T> pre) : 功能将集合中的每一个数据通过参数pre给出的规则进行筛选 list.stream().filter(x->x.startsWith("张")&&x.length()==3).forEach( System.out :: println); System.out.println(list.stream().count());// 7 // 2)跳过前三个人名, 剩下的所有姓张的,名字有2个字的人名获取到, 并遍历 list.stream().skip(3).filter(x->x.startsWith("张")&&x.length()==2).forEach( x->System.out.println(x)); // 3) 只留取集合中的前2个人名, 筛选出姓张的人名, 并遍历 list.stream().limit(2).filter(x->x.startsWith("张")).forEach(System.out :: println); // 4) map(Function<T,R> fun) : 将流资源中所有的T类型数据转换成R类型数据,返回值类型Stream<T> , 因此方法调用完毕还可以链式调用 list.stream().map(x->x+1).forEach(System.out :: print); } } |