函数式接口
一、函数式接口
1. 概念
-
**函数式接口:**有且仅有一个抽象方法的接口,称之为函数式接口。
-
接口中可以包含其他方法:默认、静态、私有。
-
@FunctionalInterface
注解-
作用:可以检测接口是否是一个函数式接口
- 是:编译成功
- 否:编译失败(接口中没有抽象方法或抽象方法的个数 )
-
格式:
@FunctionalInterface public interface MyFunctionalInterface { //定义一个抽象方法 public abstract void method(); }
-
2. 函数式接口的使用
-
一般可作为方法的参数和返回值类型。
//接口 public interface MyFunctionalInterface { public abstract void method(); } //接口的实现类 public class MyFunctionalInterfaceImpl implements MyFunctionalInterface { @Override public void method() { System.out.println("使用接口的实现类重写接口的方法"); } } //测试类 public class Test { //函数式接口作为方法的参数 public static void show(MyFunctionalInterface myFunctionalInterface) { myFunctionalInterface.method(); } public static void main(String[] args) { //使用接口的实现类作为参数 show(new MyFunctionalInterfaceImpl()); //使用匿名内部类作为参数 show(new MyFunctionalInterface() { @Override public void method() { System.out.println("使用匿名内部类重写接口的方法"); } }); //使用lambda表达式作为参数 show(()->{ System.out.println("使用lambda表达式重写接口的方法"); }); //使用简化的lambda表达式作为参数 show(()-> System.out.println("使用简化的lambda表达式重写接口的方法")); } }
二、函数式编程
1. Lambda的延迟执行
- 有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而
Lambda
表达式是延迟执行的,这正好可以作为解决方案,提升性能。
2. 性能浪费的日志案例
/*
以下代码存在一些性能浪费的问题:
调用showLog方法,传递的第二个参数是一个拼接好的字符串,
先把字符串拼接好,然后调用showLog方法,
如果showLog方法中第一个参数传递的日志等级不是1级,
那么就不会输出拼接后的字符串,
这样字符串就白白拼接了,造成了性能浪费。
*/
public class Logger {
public static void showLog(int level, String message) {
if (level == 1) {
System.out.println(message);
}
}
public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showLog(1,msg1+msg2+msg3);
showLog(2,msg1+msg2+msg3);
}
}
3. 使用Lambda表达式优化日志案例
-
Lambda
的特点:延迟执行。 -
Lambda
的使用前提:必须存在函数式接口。/* 使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中, 只有满足条件(日志的等级为1), 才会调用接口MessageBuilder中的方法builderMessage, 才会进行字符串的拼接; 如果条件不满足(日志的等级不为1), 那么接口MessageBuilder中的方法builderMessage不会执行, 所以拼接字符串的代码也不会执行; 所以不会存在性能的浪费。 */ public class Lambda { public static void showLog(int level, MessageBuilder message) { if (level == 1) { System.out.println(message.builderMessage()); } } public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; showLog(1,()->{ return msg1+msg2+msg3; }); showLog(2,()->{ return msg1+msg2+msg3; }); } }
三、常用函数式接口
1. Supplier接口
-
java.util.function.Supplier<T>
:接口仅包含一个无参的方法T get()
,用来获取一个泛型参数指定类型的对象数据。 -
Supplier<T>
接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get
方法就会生产什么类型的数据。import java.util.function.Supplier; public class Test { //生产一个String类型的数据 public static String getString(Supplier<String> supplier) { return supplier.get(); } public static void main(String[] args) { //使用Lambda表达式 String str1 = getString(()->{ return "Hello,World!"; }); System.out.println(str1); //使用简化的Lambda表达式 String str2 = getString(()->"Hello,World!"); System.out.println(str2); } }
-
练习:求数组元素最大值
/* 使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值 */ import java.util.function.Supplier; public class Test { public static int getMax(Supplier<Integer> supplier) { return supplier.get(); } public static void main(String[] args) { int[] arr = {1,-5,7,2,10,3}; int maxValue = getMax(()->{ int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } return max; }); System.out.println(maxValue); } }
2. Consumer接口
-
java.util.function.Consumer<T>
接口则正好与Supplier
接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。 -
Consumer
接口中包含抽象方法void accept(T t)
,意为消费一个指定泛型的数据。至于如何消费(使用),需要自定义。import java.util.function.Consumer; public class Test { public static void method(String name, Consumer<String> consumer) { consumer.accept(name); } public static void main(String[] args) { //输出字符串 method("zhuguli",(String name)->{ System.out.println(name); // zhuguli }); //翻转字符串 method("zhuguli",(String name)->{ String str = new StringBuffer(name).reverse().toString(); System.out.println(str); // iluguhz }); } }
-
默认方法:
andThen
-
作用:需要两个或多个
Consumer
接口,可以把两个或多个Consumer
接口组合到一起,再对数据进行消费。 -
例如:
Consumer<String> consumer1; Consumer<String> consumer2; String str; consumer1.accept(str); consumer2.accept(str); //使用andThen consumer1.andThen(consumer2).accept(str);
-
consumer1.andThen(consumer2).accept(str);
谁写在前边谁先消费。
import java.util.function.Consumer; public class Test { public static void method(String str, Consumer<String> consumer1, Consumer<String> consumer2){ consumer1.andThen(consumer2).accept(str); } public static void main(String[] args) { String str = "Hello"; method(str,(String s)->{ System.out.println(s.toUpperCase()); // HELLO },(String s)->{ System.out.println(s.toLowerCase()); // hello }); } }
-
-
练习:格式化打印信息
/* 字符串数组中存有多条信息,请按照格式”姓名:xx,年龄:xx。“的格式将信息打印出来。 要求: 将打印姓名的动作作为第一个Consumer接口的Lambda实例; 将打印年龄的动作作为第二个Consumer接口的Lambda实例; 将两个Consumer接口按照顺序”拼接“到一起。 */ import java.util.function.Consumer; public class Test { public static void printInfo(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2){ for (String str : arr) { consumer1.andThen(consumer2).accept(str); } } public static void main(String[] args) { String[] arr = {"朱古力,18", "猪猪侠,20", "猪猪猪,22"}; printInfo(arr, (String str)->{ String name = str.split(",")[0]; System.out.print("姓名:" + name + ","); }, (String str)->{ String age = str.split(",")[1]; System.out.println("年龄:" + age + "。"); }); } }
3. Predicate接口
-
java.util.function.Predicate<T>
接口- 作用:对某种数据类型的数据进行判断,结果返回一个
boolean
值。
- 作用:对某种数据类型的数据进行判断,结果返回一个
-
抽象方法:
boolean test(T t)
- 作用:用来对指定的数据类型数据进行判断的方法。
- 结果:
- 符合条件,返回
true
; - 不符合条件,返回
false
。
- 符合条件,返回
import java.util.function.Predicate; public class Test01 { public static boolean checkString(String str, Predicate<String> predicate) { return predicate.test(str); } public static void main(String[] args) { //检测字符串的长度是否大于3 String str = "hello"; boolean bool = checkString(str,(String string)->{ return string.length() > 3; }); System.out.println(bool); // true } }
-
默认方法:
and
- 作用:表示并且关系,用于连接两个判断条件
/* 定义一个方法,方法的参数: 一个字符串; 一个用于判断字符串的长度是否大于5; 一个用于判断字符串中是否包含a 必须同时满足两个条件 */ import java.util.function.Predicate; public class Test02 { public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2){ return pre1.and(pre2).test(str); // 等价于 return pre1.test(str) && pre2.test(str); } public static void main(String[] args) { String str = "abcdef"; boolean bool = checkString(str, (String string)->{ return string.length() > 5; }, (String string)->{ return str.contains("a"); }); System.out.println(bool); // true } }
-
默认方法:
or
- 作用:表示或关系,用于连接两个判断条件
/* 定义一个方法,方法的参数: 一个字符串; 一个用于判断字符串的长度是否大于5; 一个用于判断字符串中是否包含a 必须同时满足两个条件 */ import java.util.function.Predicate; public class Test03 { public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2){ return pre1.or(pre2).test(str); // 等价于 return pre1.test(str) || pre2.test(str); } public static void main(String[] args) { String str = "bcdefg"; boolean bool = checkString(str, (String string)->{ return string.length() > 5; }, (String string)->{ return str.contains("a"); }); System.out.println(bool); // true } }
-
默认方法:
negate
- 作用:表示取反
/* 定义一个方法,方法的参数: 一个字符串; 一个用于判断字符串的长度是否大于5; */ import java.util.function.Predicate; public class Test04 { public static boolean checkString(String str, Predicate<String> pre){ return pre.negate().test(str); // 等价于 return !pre1.test(str) ; } public static void main(String[] args) { String str = "bcdefg"; boolean bool = checkString(str, (String string)->{ return string.length() > 5; }); System.out.println(bool); // false } }
-
练习:集合信息筛选
/* 数组当中有多条“姓名+性别”的信息如下: String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"}; 请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中, 需要同时满足两个条件: 必须为女生 姓名为四个字 */ import java.util.ArrayList; import java.util.function.Predicate; public class Test { public static ArrayList<String> checkArray(String[] arr, Predicate<String> pre1, Predicate<String> pre2) { ArrayList<String> list = new ArrayList<>(); for (String s : arr) { if (pre1.and(pre2).test(s)) { list.add(s); } } return list; } public static void main(String[] args) { String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"}; ArrayList<String> list = checkArray(array,(String string)->{ return string.split(",")[1].equals("女"); },(String string)->{ return string.split(",")[0].length() == 4; }); for (String s : list) { System.out.println(s); } } }
4. Function接口
java.util.function.Function<T,R>
接口:用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。- 抽象方法:
R apply(T t)
,根据类型T
的参数获取类型R
的结果。
//将String类型转换为Integer类型
import java.util.function.Function;
public class Test01 {
public static void change(String str, Function<String, Integer> func) {
Integer in = func.apply(str);
System.out.println(in + 10); // 30
}
public static void main(String[] args) {
String s = "20";
change(s, (String str)->{
return Integer.parseInt(s);
});
}
}
- 默认方法:
andThen
用来进行组合操作。
/*
把String类型的“123”,转换为Integer类型,把转换的结果加10;
把增加之后的Integer类型的数据,转换为String类型
*/
import java.util.function.Function;
public class Test02 {
public static void change(String str, Function<String,Integer> func1, Function<Integer,String> func2){
String s = func1.andThen(func2).apply(str);
System.out.println(s); // "133"
}
public static void main(String[] args) {
String str = "123";
change(str,(String s)->{
return Integer.parseInt(s) + 10;
},(Integer i)->{
return i + "";
});
}
}
- 练习:自定义函数模型拼接
/*
请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
String str = "朱古力,22";
1.将字符串截取数字部分,得到字符串;
2.将上一步的字符串转换为int类型的数字;
3.将上一步的int数字累加100,得到结果int数字。
*/
import java.util.function.Function;
public class Test {
public static void change(String str, Function<String,String> func1,
Function<String,Integer> func2,
Function<Integer,Integer> func3 ) {
int i = func1.andThen(func2).andThen(func3).apply(str);
System.out.println(i);
}
public static void main(String[] args) {
String str = "朱古力,22";
change(str,(String s)->{
return s.split(",")[1];
},(String s)->{
return Integer.parseInt(s);
},(Integer i)->{
return i + 100;
});
}
}
转载:https://blog.csdn.net/weixin_44781238/article/details/106135657
查看评论