目录
Lambda表达式
Lambda表达式的样例
Lambda的使用前提
函数式接口
函数式编程
常用函数式接口
1.Supplier接口
2.Consumer接口
3.Predicate接口
4.Function接口
编程思想
面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。
函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。
JDK1.8+中加入了lambda表达式!
// 面向对象的编程思想和函数式编程思想的比较
public class ObjectAndFunction {
public static void main(String[] args) {
// 匿名内部类(面向对象编程)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("面向对象式的任务执行");
}
}).start();
// 使用lambda表达式(函数式编程)
new Thread(()-> System.out.println("面向函数式的任务执行")).start();
}
}
匿名内部类(面向对象的编程方式)的冗余体现:
相比起来,lambda表达式(函数式编程)的更加简洁,不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担。
Lambda省去面向对象的条条框框,格式由3个部分组成:一些参数、一个箭头、一段代码。
表达式的标准格式:
(参数类型 参数名称) -> {代码语句}
表达式的省略格式:
1.无参无返回
给定一个厨子接口Cook,包含一个抽象方法makeFood,这个抽象方法没有参数也没有返回值。需求:在下面的代码中请使用Lambda表达式调用invokeCook方法打印输出“我要吃饭!”。
// 题目中给出的Cook接口
public interface Cook{
public abstract void makeFood();
}
// 题目中给出的测试类,并且里面设置了填空,需要填写lambda表达式
public class InvokeCook{
public static void main(String[] args){
// 此处填写lambda表达式
}
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
解答:使用lambda调用invokeCook,小括号代表参数为空,表示的是Cook 接口makeFood 抽象方法的参数,大括号代代码体代表makeFood 的方法体。
public static void main(String[] args) {
// 无参数无返回值的面向对象式编程
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("面向对象式的使用");
}
});
// 无参数无返回值的函数式编程
invokeCook(()-> {System.out.println("函数式lambda表达式标准格式的使用");});
invokeCook(()-> System.out.println("函数式lambda表达式省略格式的使用"));
}
2.有参有返回
需求:使用数组存储多个Person对象,对数组中的Person对象使用Arrays数组工具类的sort方法通过年龄进行升序排列。
解答:下面是使用lambda的表达式(标准格式和省略格式)和面向对象的方式对比。
// 定义一个Person类,实现Comparable接口并重写compareTo方法
public class Person implements Comparable {
private String name;
private int age;
private int nice; // 美貌值 0-10
@Override // 要使用默认的sort规则排序,就必须重写Comparable接口中的compareTo方法。
public int compareTo(Person o) {
// return 0; //原来的代码恒返回0,表示不需要交换未知也就是不进行排序返回原序列。
if (this.age > o.age) { // 只对年龄进行了排序
return 1; // 返回正序。原来的顺序:this.age是当前元素,o.age是下一个元素。return 1需要交换位置。
}
if (this.age < o.age) {
return -1;
}
return 0;
// 等效于下面这句
// return this.age-o.age; // this.age永远是当前的元素,o.age是下一个元素。当为正的时候,即return 1.需要交换顺序。其余情况则不需要交换顺序。
/* 当 this.age>o.age时,年龄按升序排序。
所以return this.age-o.age时,年龄升序排序。
当 this.age(){
@Override
public int compare(Integer o1, Integer o2) {
return 0;
}
}
);
System.out.println("从大到小:"+Arrays.toString(array)); // 从大到小:[23, 11, 10, 5]
// array数组中的元素是自定义类型/对象
Person[] persons = {new Person("赵丽颖",20,7),new Person("迪丽热巴",18,9),new Person("古力娜扎",19,8)};
// 要使用默认规则排序,被排序的array数组里面存储的元素,必须实现Comparable接口,重写接口中的compareTo方法定义排序的规则。
Arrays.sort(persons); // 进行排序,排序规则已经重写,只对年龄排序,是升序排序。
System.out.println("面向对象,只对年龄升序排序:"+Arrays.toString(persons)); // 面向对象,只对年龄升序排序:[Person{name='迪丽热巴', age=18, nice=9}, Person{name='古力娜扎', age=19, nice=8}, Person{name='赵丽颖', age=20, nice=7}]
// 面向对象编程。使用第三方的,不需要去继承Comparable类和重写compareTo方法,只需要传入Comparator对象和重写compare方法。
Arrays.sort(persons, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.getNice()-o2.getNice();// 美貌值从小到大排序
}
});
System.out.println("面向对象,只对美貌值升序排序:"+Arrays.toString(persons)); // 面向对象,只对美貌值升序排序:[Person{name='赵丽颖', age=20, nice=7}, Person{name='古力娜扎', age=19, nice=8}, Person{name='迪丽热巴', age=18, nice=9}]
// 函数式编程,使用lambda表达式的标准格式
Arrays.sort(persons,(Person o1,Person o2)->{return o2.getAge()-o1.getAge();});
System.out.println("lambda的标准格式,只对美貌值降序排序:"+Arrays.toString(persons)); // lambda的标准格式,只对美貌值降序排序:[Person{name='赵丽颖', age=20, nice=7}, Person{name='古力娜扎', age=19, nice=8}, Person{name='迪丽热巴', age=18, nice=9}]
// 相比于标准格,(1)小括号内参数的类型可以省略(2)大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
Arrays.sort(persons,(o1,o2)->o1.getAge()-o2.getAge());
System.out.println("lambda的省略格式,只对美貌值升序排序:"+Arrays.toString(persons)); // lambda的省略格式,只对美貌值升序排序:[Person{name='迪丽热巴', age=18, nice=9}, Person{name='古力娜扎', age=19, nice=8}, Person{name='赵丽颖', age=20, nice=7}]
}
}
有且仅有一个抽象方法的接口,称为“函数式接口”。
1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
备注:无论是JDK内置的Runnable 、Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
2. 使用Lambda必须具有上下文推断。
备注:也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
概念:函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导(也是Lambda使用前提)。
“语法糖”:指的是使用更加方便,但是原理不变的代码语法。
举例:for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。
函数式接口的格式:
只要确保接口中有且仅有一个抽象方法即可:其中抽象方法中的public abstract这两个关键字是可以省略的。
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}
@FunctionalInterface注解(JDK1.8+):
与@Override 注解的作用类似(保险栓),Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上。一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。
需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
函数式接口的典型使用场景就是作为方法的参数
// 接口定义
@FunctionalInterface // 函数式接口加保险栓,编译检查
public interface MyInterface {
public abstract void method();
}
// 接口实现类的定义
public class MyInterfaceImpl implements MyInterface{
@Override // 保险栓
public void method() {
System.out.println("使用实现类重写接口中的抽象方法");
}
}
// 函数式接口的使用
public class MyInterfaceFunction {
public static void main(String[] args) {
// (1)方法的参数是一个接口,所以可以传递接口的实现类对象
show(new MyInterfaceImpl());
// (2)调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类
show(new MyInterface() {
@Override
public void method() {
System.out.println("使用匿名内部类重写接口中的抽象方法");
}
});
// (3)方法的参数是一个函数式接口,所以可以使用Lambda表达式
show(()-> System.out.println("使用Lambda表达式重写接口中的抽象方法"));
}
// 函数式接口的使用:一般可以作为方法的参数和返回值类型
public static void show(MyInterface myInter){
myInter.method();
}
}
在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式(函数式编程)是延迟执行的,这正好可以作为解决方案,提升性能。
性能浪费的例子:
日志案例。日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。
public class Logger {
public static void main(String[] args) {
String msgA = "迪丽热巴";
String msgB = "中国女性新力量";
String msgC = "优秀的人";
log(1,msgA+msgB+msgC);
}
public static void log(int level, String msg){
if(level==1){
System.out.println(msg);
}
}
}
这段代码存在问题:无论级别是否满足要求,作为log 方法的第二个参数,三个字符串一定会首先被拼接传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。
Lambda写法进行改进:只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。
// 定义函数接口
@FunctionalInterface
public interface MessageBuilder {
public abstract String builderMessage();
}
// Lambda表达式使用
public class LoggerLambda {
public static void main(String[] args) {
String msgA = "迪丽热巴";
String msgB = "中国女性新力量";
String msgC = "优秀的人";
log(1,()->msgA+msgB+msgC);
// 验证:在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。
log(2,()->{
System.out.println("Lambda执行!");
return msgA+msgB+msgC;
}); // 实际上不执行打印。
}
public static void log(int level, MessageBuilder builder){
if(level==1){
System.out.println(builder.builderMessage());
}
}
}
java.util.function.Supplier
Supplier
import java.util.function.Supplier;
public class SupllierTest {
public static void main(String[] args) {
// 调用getString方法,方法参数Supplier是一个函数式接口,所以可以考虑使用Lambda表达式
String s1 = getString(()->{return "迪丽热巴";});
// 优化Lambda表达式
String s2 = getString(()->"古力娜扎");
System.out.println(s1+s2); // 迪丽热巴古力娜扎
}
// 方法的参数传递Supplier接口,泛型执行String,那么get方法就会返回一个String
public static String getString(Supplier sup){
return sup.get();
}
}
练习:求数组最大值,使用该接口实现
// 使用Supplier接口求数组元素中的最大值
import java.util.function.Supplier;
public class SupllierTest {
public static void main(String[] args) {
int[] array = {7,8,-1,10,44,23,2};
// 调用getMax方法,方法参数Supplier是一个函数式接口,所以可以考虑使用Lambda表达式
int maxValue = getMax(()->{
int max = array[0];
for (int i : array) {
if(i>max){
max = i;
}
}
return max;
});
System.out.println(maxValue);
}
// 方法的参数传递Supplier接口,泛型执行Integer,那么get方法就会返回一个int
public static int getMax(Supplier sup){
return sup.get();
}
}
java.util.function.Consumer
(1)抽象方法:accept
(仅有一个)抽象方法:void accept(T t) ,意为消费一个指定泛型的数据。
Consumer接口是一个消费性接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据,至于具体怎么消费(或者说使用),需要自定义(比如定义输出、计算....)
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
// 调用method方法,传递字符串姓名,方法的另一个参数是Consumer接口,是一个函数式接口,所以可以传递Lambda表达式
method("迪丽热巴",(String name)->{
// 对传递的字符串进行消费
// 消费方式:直接输出
System.out.println(name);
// 消费方式:把字符串进行反转输出
String reName = new StringBuffer(name).reverse().toString();
System.out.println(reName);
});
}
// 定义一个方法,方法的参数传递一个字符串的姓名,另一个参数传递Consumer接口,泛型使用String,可以使用Consumer接口消费字符串的姓名。
public static void method(String name, Consumer con){
con.accept(name);
}
}
(2)默认方法:andthen
default Consumer
如果一个方法的参数和返回值全都是Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer 接口中的default方法andThen 。
作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费。 当要连接多个consumer接口时,andThen可以使用链式写法:例如con1.andThen(con2.andThen(con3)).accept(消费的内容) 执行顺序依旧遵照从外到里。
例如:
Consumercon1
Consumercon2
String s = "迪丽热巴";
con1.accept(s);
con2.accept(s);
// 上面两条语句等同下面的一条语句。连接两个Consumer接口,再进行消费。
con1.andThen(con2).accept(s); // 注意:谁写外面,谁先消费。在这里是con1先消费,然后con2再消费。
下面是JDK的源代码:
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) ‐> { accept(t); after.accept(t); };
}
/*备注: java.util.Objects 的requireNonNull 静态方法将会在参数为null时主动抛出
NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
*/
下面是当方法中传入两个Consumer接口,用andThen连接的示例:
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
// 调用method方法,传递字符串姓名,方法的另一个参数是Consumer接口,是一个函数式接口,所以可以传递Lambda表达式
method("AngelaBabe",(t)->{
// 对传递的字符串进行消费, 消费方式:把字符串大写输出
System.out.println(t.toUpperCase());
},(s)->{
// 对传递的字符串进行消费, 消费方式:把字符串小写输出
System.out.println(s.toLowerCase());
});
}
// 定义一个方法,方法的参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用String类型。
public static void method(String s, Consumer con1, Consumer con2){
// 这里没有使用andThen默认方法,那么执行顺序就是con1前,con2在后
con1.accept(s);
con2.accept(s);
System.out.println("---------------------------");
// 使用andThen方法,可以简写上面两句,将两个Consumer接口连接到一起,并且执行顺序是外面在先里面在后。所以这里的执行顺序是con2在前,con1在后
con2.andThen(con1).accept(s);
}
}
/*
输出:
ANGELABABE
angelababe
---------------------------
angelababe
ANGELABABE
*/
练习:格式化打印信息
题目:下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer 接口的Lambda实例,将打印性别的动作作为第二个Consumer 接口的Lambda实例,将两个Consumer 接口按照顺序“拼接”到一起。
public static void main(String[] args) {
String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };
}
解答:
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
String[] array = {"迪丽热巴,女","古力娜扎,女","赵丽颖,女","张杰,男"};
method(array,
(s)-> System.out.println("性别:"+s.split(",")[1]+"。"),
(m)-> System.out.print("姓名:"+m.split(",")[0]+"。")); // 注意这里使用的是不换行的print
}
// 定义一个方法,方法的参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用String类型。
public static void method(String[] s, Consumer con1, Consumer con2){
// 使用andThen方法,可以简写上面两句,将两个Consumer接口连接到一起,并且执行顺序是外面在先里面在后。所以这里的执行顺序是con2在前,con1在后
for (String item : s) {
con2.andThen(con1).accept(item);
}
}
}
/*
输出:
姓名:迪丽热巴。性别:女。
姓名:古力娜扎。性别:女。
姓名:赵丽颖。性别:女。
姓名:张杰。性别:男。
*/
java.util.function.Predicate
(1) 抽象方法: test
Predicate 接口中仅包含一个抽象方法: boolean test(T t) 。用来对指定类型数据进行判断的方法。如果符合条件就返回true,如果不符合就返回false。
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
String s1 = "abcde";
String s2 = "abcdef";
// 调用checkString方法对字符串进行校验,参数传递字符串和Lambda表达式。
boolean b1 = checkString(s1,(str)->{
// 对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回。
return str.length()>5;
});
// 优化Lambda表达式
boolean b2 = checkString(s2,str->
// 对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回。
str.length()>5);
// 调用negCheckString方法,和上面条件一样时,进行取反
boolean b1_1 = negCheckString(s1,str->
str.length()>5);
boolean b2_2 = negCheckString(s2,(str)->{
return str.length()>5;
});
System.out.println("b1判断结果:"+b1+"。b2判断结果:"+b2); // b1判断结果:false。b2判断结果:true
System.out.println("b1_1判断结果:"+b1_1+"。b2_2判断结果:"+b2_2); // b1_1判断结果:true。b2_2判断结果:false
}
// 定义一个方法,参数传递一个String类型的字符串;同时传递一个Predicate接口,泛型使用String。使用Predicate中的方法test对字符串进行判断,并把判断的结果返回
public static boolean checkString(String s, Predicate pre){
return pre.test(s);
}
// 定义一个取反negate方法。
public static boolean negCheckString(String s, Predicate pre){
// 如果判断条件不变,则等效于 !pre.test(s);
return pre.negate().test(s); // 对条件成真时取反
}
}
(2)默认方法:and
回忆一下逻辑表达式:可以连接多个判断条件
&&:与运算符,有false则false。
||:或运算,有true则true。
!:非(取反)运算符,非真则假,非假则真。
Predicate接口中有一个默认方法and,表示并且的逻辑关系,也可以用于两个判断条件。其JDK源码为:
default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}
上述JDK源码方法内部的两个判断条件也是使用&&运算符连接起来的。
例如:
Predicatepre1 // 用来判断是否字符串的长度大于5
Predicatepre2 // 用来判断字符串中是否包含a
String s = "abcdef";
pre1.test(s) && pre2.test(s); // 连接了两个判断方法,必须同时满足。
// 上面两条语句等同下面的一条语句。连接两个Predicate接口,再进行判断。
pre1.and(pre2).test(s); // 注意:谁写外面,谁先判断。在这里是pre1先判断,然后pre2再判断。
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
String s1 = "abcde";
String s2 = "abcdef";
// 调用checkString方法对字符串进行校验,参数传递字符串和Lambda表达式。
boolean b1 = checkString(s2,(String str)->{
// 对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回。
return str.length()>5;
}, // 对参数传递的字符串进行判断,判断字符串的是否包含字母a,并把判断的结果返回。
str->str.contains("a"));
System.out.println("b1判断结果:"+b1/*+"。b2判断结果:"+b2*/); // b1判断结果:true
}
// 定义一个方法,参数传递一个String类型的字符串;同时传递一个Predicate接口,泛型使用String。使用Predicate中的方法test对字符串进行判断,并把判断的结果返回
public static boolean checkString(String s, Predicate pre1,Predicate pre2){
boolean a = pre1.test(s) && pre2.test(s);
boolean b = pre1.and(pre2).test(s);
System.out.println("使用&&运算符的结果:"+a+" 使用and方法的结果:"+b);
return a&&b;
}
}
(3)默认方法:or
与and 的“与”类似,默认方法or 实现逻辑关系中的“或”。JDK源码为:
default Predicate or(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) || other.test(t);
}
(4)默认方法:negate
“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法negate 的JDK源代码为:
default Predicate negate() {
return (t) ‐> !test(t);
}
练习:集合信息筛选
题目:数组当中有多条“姓名+性别”的信息如下,请通过Predicate 接口的拼装将符合要求的字符串筛选到集合ArrayList 中,需要同时满足两个条件:1. 必须为女生;2. 姓名为4个字。
public class DemoPredicate {
public static void main(String[] args) {
String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
}
}
解答:1.有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断。2.必须同时满足两个条件,所以可以使用and方法连接两个判断条件。
import java.util.function.Predicate;
import java.util.ArrayList;
public class PredicateTest {
public static void main(String[] args) {
String[] array = {"迪丽热巴,女","古力娜扎,女","赵丽颖,女","马儿扎哈,男"};
ArrayList list = checkString(array,
s->s.split(",")[0].length()==4,
s->s.split(",")[1].equals("女")
);
System.out.println(list.toString()); // [迪丽热巴,女, 古力娜扎,女]
}
// 定义一个方法,参数传递一个String类型的数组和两个Predicate接口,泛型使用String,对数组中的信息进行过滤。把满足条件的信息存到ArrayList集合中返回。
public static ArrayList checkString(String[] strs, Predicate pre1, Predicate pre2) {
ArrayList array = new ArrayList<>();
for (String str : strs) {
if (pre1.and(pre2).test(str)) {
array.add(str);
}
}
return array;
}
}
java.util.function.Function
(1)抽象方法:apply
Function 接口中仅有的抽象方法为: R apply(T t) ,根据类型T的参数获取类型R的结果。
作用:将T类型转换成R类型。
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
// 定义一个字符串类型的额整数
String s = "12345";
// 调用change方法,传递字符串类型的整数和Lambda表达式
change(s,str->Integer.parseInt(str)); // 输出得到的是12365
}
// 定义一个方法。方法的参数传递一个字符串类型的整数;方法的参数传递一个Function接口,泛型使用;使用Function接口中的方法apply,把字符串类型的整数,转换为Integer类型的整数。
private static void change(String s, Function fun) {
Integer num = fun.apply(s);
System.out.println(num+20); // 如果是字符串则数字20不会进行运算,而是直接添加在字符换后面。如果是数字,那么会运算。
}
}
(2)默认方法:andThen
Function 接口中有一个默认的andThen 方法,用来进行组合操作。JDK源代码如:
default Function andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) ‐> after.apply(apply(t));
}
需求:把String类型的“123”,转换为Integer类型,加10,结果在转换为String类型
/*
使用默认方法andThen来将多种转换进行连接,组合
*/
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
String s = "123";
String result = change(s,
// 将字符串转化为整数+10
string -> Integer.parseInt(string)+10,
// 将整数转化成字符串
integer -> integer + "");
System.out.println(result); // 133
}
// 定义一个方法。方法的参数传递一个字符串类型的整数;方法的参数传递一个Function接口,泛型使用;使用Function接口中的方法apply,把字符串类型的整数,转换为Integer类型的整数。
private static String change(String s, Function fun1,Function fun2) {
Integer num = fun1.apply(s);
String str1 = fun2.apply(num);
// 等效于下面的一句
String str2 = fun1.andThen(fun2).apply(s); // 从外到里的顺序。
System.out.println(str1.equals(str2)); // true
return str2;
}
}
练习:自定义函数模型拼接
题目:请使用Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为:String str = "赵丽颖,20"。(1) 将字符串截取数字年龄部分,得到字符串;(2) 将上一步的字符串转换成为int类型的数字;(3) 将上一步的int数字加100,得到结果int数字。
解答:上面的要求一一对应(1)Function
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
String s = "赵丽颖,20";
int result = change(s,
// 将字符串转化为整数+10
str1 -> str1.split(",")[1],
// 将整数转化成字符串
str2-> Integer.parseInt(str2),
num ->num+100);
System.out.println(result); // 120
}
private static int change(String s, Function fun1,Function fun2, Function fun3) {
int result = fun1.andThen(fun2.andThen(fun3)).apply(s);
return result;
}
}