深入Lambda
(day19)
今日内容:
一:函数式接口
1.什么是函数表达式接口
1)接口中只有一个抽象方法
2)Lambda表达式实现的前提条件是:只能做为函数式接口的实现类对象
3)函数式接口为了封装某一个方法而存在
举例 : 客户要求定义出一个方法功能, 对两个整数a和b做任意操作
客户1 : 求两数的和
客户2 : 求两数的差
客户3 : 求两数而定乘积
... 无数的客户,要对于这个一个方法实现任意的a和b的两数操作
于是上述案例,不仅仅需要a和b两个整数,还需要对于a和b两个数的处理方式,函数式接口就可以将数据的处理方法封装起来,从而将函数式接口作为方法参数进行传递。
二:JDK8中四种函数式接口
1.消费型接口
Consumer<T>
void accept(T t);
在java中消费概念就是使用参数t
如果需要传递对于数据的消费方式,处理方式,那么Consumer可以作为方法的参数传递
public class Demo01_消费型接口 {
public static void main(String[] args) {
// 1.使用lambda表达式作为函数式接口Consumer的一个实现类对象
// 客户1:花了500,买了一把大宝剑
Consumer con = a -> System.out.println("客户花了" + a + "买了一把大宝剑");
testConsumer(500, con);
// 客户1:花了600,买了一堆化妆品
Consumer con1 = b -> System.out.println("客户花了" + b + "买了一堆化妆品");
testConsumer(600, con1);
}
/*
* 定义出一个方法功能, 客户消费500元现金, 每一个客户消费方法不同
* 1)客户1 : 花了500元, 买了一把大宝剑
* 2)客户2 : 花了500元,买了一堆化妆品
* 3)客户3 : 花了500元, 买了一双球鞋
* 4)客户4 : 花了500元, 捐献
* 5)还有无数的客户,对于500元消费各不相同
*/
/*
* 分析:
* 1.将客户消费金额作为第一个参数 int money
* 2.客户数量很多,每一个客户对于钱的消费方式不同,提供对于钱的消费方式
*/
public static void testConsumer(int money, Consumer<Integer> con) {
//Consumer接口中,只有一个抽象方法accept(T t):方法就是为了消费参数t
con.accept(money);
}
}
2.供给型接口
Supplier<T>
T get();
方法功能主要产出一个T类型的数据,如果没有方法的参数列表,但是需要获取到指定的结果类型T,那么Supplier接口可以作为方法参数传递
public class Demo02_供给型接口 {
public static void main(String[] args) {
//客户1:装5个数据,30~80之间的随机数
Supplier<Integer> sup = ()->{
Random ran = new Random();
Integer k = ran.nextInt(51)+30;
return k;
};
ArrayList list1 = getSupplier(5,sup);
System.out.println(list1);
//客户2:装8个数据,1~100之间的随机偶数
Supplier<Integer> sup1 = ()->{
Random ran1 = new Random();
while(true) {
Integer s = ran1.nextInt(100)+1;
if(s%2==0) {
return s;
}
}
};
ArrayList list2 = getSupplier(8,sup1);
System.out.println(list2);
}
/* 定义出一个方法功能, 能给客户返回ArrayList<Integer>容器,中装有数据. 容器中装集合数据,由客户决定;
* 容器中承装数据有什么规律, 由客户决定;
1)客户1 : 装5个数据, 30-80之间的随机数
2)客户2 : 装8个数据, 1-100之间的随机偶数
3)...*/
/*
*分析:
*1.第一个方法参数,作为容器中承装的数据个数con
*2.第二个方法参数,需要存储在容器中的整数存在规则
**/
public static ArrayList<Integer> getSupplier(int con,Supplier<Integer> sup){
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<con;i++) {
//list需要一个Integer类型的数据
//T get()->T Integer
list.add(sup.get());
}
return list;
}
}
3.函数型接口
Function<T,R>
R apply(T t);
1) R apply(T t)是抽象方法,主要功能是提供一个T类型参数,返回一个R类型结果,如果有一个参数t,需要获取到另外一个结果r,可以将function接口作为方法的参数进行传递
2) 默认方法:andThen(Function<T,R> fun)
将方法调用者Function实现类的计算结果,作为参数fun的T类型参数继续运算。
Function fun1
Function fun2
fun1.andThen(fun2)----->相当于fun2.apply(fun1.apply)
public class Demo03_函数型接口 {
public static void main(String[] args) {
/*定义一个方法功能, 根据整数x,获取出对应的整数y, y数据的获取方式由客户决定
1)客户1 : y 为x的2倍
2)客户2 : y 与 x 相等
3)客户3 : y 为 x 的平方
4)....*/
//客户1 : y 为x的2倍
Function<Integer,Integer> fun = x->x*2;
System.out.println(getFunction(10,fun));
//客户2 : y 与 x 相等
Function<Integer,Integer> fun1 = x->x;
System.out.println(getFunction(10,fun1));
//客户3 : y 为 x 的平方
Function<Integer,Integer> fun2 = x->x*x;
System.out.println(getFunction(10,fun2));
System.out.println("我是分割线------------------------------------------------");
/*案例2:
定义一个方法功能, 给出一个字符串s, 需要将字符串转成对应的整数,
转换之后通过计算获取到另外一个整数y, y数据的计算方式由客户决定(结合使用andThen方法功能)
1)客户1 : 将”6”, 得到”6”的3倍
2)客户2 : 将”-2”, 得到-2 + 1
3)...*/
Function<String,Integer> fuun = s->Integer.parseInt(s);
Function<Integer,Integer> fuun2 = s->s*3;
System.out.println(getFunction2("6",fuun,fuun2));
}
/*
* 分析:
* 1.方法第一个参数表示目前具有整数t
* 2.方法第二个参数,需要目标结果y计算方式,根据t得出对应计算结果
*/
public static int getFunction(int t,Function<Integer,Integer> fun){
return fun.apply(t);
}
public static int getFunction2(String s,Function<String,Integer> fuun,Function<Integer,Integer> fuun2) {
//fuun获取到的计算结果Integer类型,作为fuun2的apply的方法参数
return fuun.andThen(fuun2).apply(s);
}
}
4.断言型接口(由于自己也没有达到了解的程度,所以此模块暂时不写)
Predicate<T>
boolean test(T t);
三:可变参数
如果定义一个方法功能时:参数类型确定,参数个数不确定,可以在方法的参数列表上设计出可变参数(参数的个数可变)
public class Demo05_可变参数 {
public static void main(String[] args) {
// 1. 可变参数可以是0个参数
System.out.println(getSum()); //空
// 2. 可变参数可以是1个参数
System.out.println(getSum(5)); //5
// 3. 可变参数可以是多个参数
System.out.println(getSum(5,6,7,8)); //26
// 4. 3表示变量s; 4和5表示可变参数is
System.out.println(getSum1(3,4,5));//9
}
//1)一个方法的参数列表中,最多只能有一个可变参数
public static int getSum(int...is) {
// is作为一个int[]数组使用
int sum = 0;
for(int s : is) {
sum = sum+s;
}
return sum;
}
//2)可变参数必须放置在参数列表的最后
public static int getSum1(int i,int...is) {
int sum = 0;
for(int s : is) {
sum = sum+s;
}
return sum;
}
}
四:方法引用
1:函数式接口:
使用Lambda表达式进行实现,如何Lambda表达式的方法体已经被某个对象中的方法实现过,不需要再Lambda表达式中再次实现相同的功能,只需要引用对应已经实现的方法即可,称为方法引用。
2:方法引用语法结构:
函数式接口 变量名 = 类对象 :: 方法名;
函数式接口 变量名 = 类名 :: 方法名;
public class Demo06_方法引用 {
public static void main(String[] args) {
// 1.使用Lambda表达式实现函数式接口PrintInter
PrintInter pi = s -> System.out.println(s);
pi.print("??????");
// 2.使用方法引用实现函数式接口:方式1
PrintInter pi2 = System.out::println;
pi2.print("大大的疑惑");
/*
* 3.使用Lambda表达式实现函数式接口PrintInter
* 要求 : 实现过程需要满足
* 1) 将参数String类型转换成int类型
* 2)将int类型+1进行输出
*/
PrintInter pi3 = s -> {
int num = Integer.parseInt(s);
System.out.println(num + 1);
};
pi3.print("13");
//4.变形,使用方法引用方式代替原有Lambda表达式语法结构
PrintInter pi4 = new PrintClass()::print;
pi4.print("90");
// new printFirst :: print;
//5.使用方法引用方式代替原有Lambda表达式语法结构(静态)
PrintInter pi5 = PrintClass2::print;
pi5.print("12345");
}
}
class PrintClass {
public void print(String s) {
int num = Integer.parseInt(s);
System.out.println(num + 1);
}
}
class PrintClass2 {
public static void print(String s) {
int num = Integer.parseInt(s);
System.out.println(num + 1);
}
}
@FunctionalInterface
interface PrintInter {
void print(String s);
}
五:StreamingAPI
1.只能用于Connection单列集合
stream():获取到集合对应的流资源,方法返回值类型Stream接口,证明方法实际返回的是接口的一个实现类方法。
Stream接口:流资源,主要功能就是对单列集合做数据的操作,因此对于集合数据中的操作封装了方法功能。
2.Stream类型中的方法功能:
1)filter(Predicate pre):
表示过滤器,做数据的筛选,通过参数pre断言型接口给出的规则进行数据筛选,返回值类型Stream,方法调用之后可以进行链式调用。
2)forEach(Consumer con):
表示遍历,遍历的方式就是参数消费型接口对于元素的消费方式,返回值类型void
3)count:
表示将目前流资源中操作的数据个数获取到,返回值类型long
4)limit(long size):
方法功能表示截取流资源中的一部分数据,截取保留流资源中从1~size的元素,返回值类型Stream类型
5)skip(long n):
跳过流资源中的n个元素,继续从n之后的元素进行操作,返回值类型Stream类型
6)map(Function<T,R> fun):
将流资源中的所有T类型数据,全部转换成R类型数据,返回值类型Stream类型
public class Demo01_Stream方法使用 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("蓝忘机");
list.add("蓝启仁");
list.add("蓝湛");
list.add("魏无羡");
list.add("花城");
// 1.先将集合name对应的Stream流资源获取到
// filter 中的参数列表 : Predicate断言型接口, 方法调用需要传递实际参数可以使用Lambda表达式实现
// s表示集合中的每一个字符串,filter本身返回一个Stream类型
// 获取打印集合中第一个字符是“蓝”,字符长度是3的数据元素
list.stream().filter(s -> s.startsWith("蓝") && s.length() == 3).forEach(s -> System.out.println(s));// 蓝忘机 蓝启仁
// 3)count: 表示将目前流资源中操作的数据个数获取到,返回值类型long
// 获取目前流资源中操作的数据个数
System.out.println(list.stream().count()); // 5
// 4)limit(long size): 方法功能表示截取流资源中的一部分数据,截取保留流资源中从1~size的元素,返回值类型Stream类
// 显示在下标3之前的符合要求的数据
list.stream().limit(3).filter(s -> s.startsWith("蓝") && s.length() == 3).forEach(s -> System.out.println(s));// 蓝忘机
// 蓝启仁
// 5)skip(long n):跳过流资源中的n个元素,继续从n之后的元素进行操作,返回值类型Stream类型
list.stream().skip(2).filter(x -> x.startsWith("蓝") && x.length() == 3).forEach(System.out::println); // 空
// 6)map(Function<T,R> fun):将流资源中的所有T类型数据,全部转换成R类型数据,返回值类型Stream类型
//这个是同类型转换
list.stream().map(x -> x + 1).forEach(System.out::println);
// 蓝忘机1 蓝启仁1 蓝湛1 魏无羡1 花城1
}
}