1、函数式接口的概念
函数式接口在java中是指:有且仅有一个抽象方法的接口,当然接口中也可以包含其他的方法(默认,静态,私有)。 函数式接口的定义:
csharp
体验AI代码助手
代码解读
复制代码
@FunctionalInterface
public interface MyFunctionalInterface {
//定义一个抽象方法
public abstract void method();
}
- 接口中的抽象方法的public abstract可以省略。
- @FunctionalInterface可以检测接口是否为函数式接口(是编译成功;否编译失败-接口中没有抽象方法或抽象方法个数多于一个)。
2.函数式接口的使用
2.1、作为方法的参数使用
typescript
体验AI代码助手
代码解读
复制代码
public class Demo01 {
public static void show(MyFunctionalInterface myFunctionalInterface){
myFunctionalInterface.method();
}
public static void main(String[] args) {
//调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("匿名内部类重写接口中的抽象方法");
}
});
//调用show方法,方法的参数是一个函数式接口,所以我们可以使用lambda表达式
show(()->{
System.out.println("使用lambda表达式重写接口中的抽象方法");
});
//简化lambda表达式
show(()-> System.out.println("使用lambda表达式重写接口中的抽象方法"));
}
}
2.2、lambda延迟执行
2.2.1、性能浪费的日志案例
typescript
体验AI代码助手
代码解读
复制代码
public class Demo02 {
public static void showLog(int level, String log){
if(level == 1){
System.out.println(log);
}
}
public static void main(String[] args){
String msg1 = "hello";
String msg2 = "world";
String msg3 = "java";
showLog(2, msg1 + msg2 + msg3);
}
}
以上代码存在一些性能浪费的问题,调用showLog方法,是先把字符串拼接好,然后再调用showLog方法,如果传入的日志等级参数不是1,则不会输出拼接口的字符串,因此就白拼接了,存在浪费。
2.2.2、日志优化
csharp
体验AI代码助手
代码解读
复制代码
@FunctionalInterface
public interface MessageBuilder {
//定义一个拼接消息的抽象方法,返回拼接后的字符串
public abstract String builderMessage();
}
arduino
体验AI代码助手
代码解读
复制代码
public class Demo03Lambda {
public static void showLog(int level, MessageBuilder mBuilder){
if(level == 1) {
System.out.println(mBuilder.builderMessage());
}
}
public static void main(String[] args){
String msg1 = "hello";
String msg2 = "world";
String msg3 = "java";
/**
* 使用lambda表达式作为参数传递,仅仅是把参数传递给showLog方法中,
* 只有满足条件,日志的等级是1,才会调用接口MessageBuilder中的方法,
* 才会进行字符串的拼接
*/
showLog(1, ()-> msg1 + msg2 + msg3);
}
}
2.3、函数式接口作为函数的参数案例
假设有一个startThread方法使用Runnable接口作为参数
typescript
体验AI代码助手
代码解读
复制代码
public class Demo04Lambda {
//定义一个方法startThread,方法的参数使用函数式接口Runnable
public static void startThread(Runnable runnable){
new Thread(runnable).start();
}
public static void main(String[] args){
//方法的参数是一个接口,可以传递这个接口的匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
//方法的参数是一个函数式接口,可以传递lambda表达式
startThread(()-> System.out.println(Thread.currentThread().getName()));
}
}
2.4、函数式接口作为函数的返回值案例
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个lambda表达式。 假设需要一个方法来获取java.util.Comparator接口的对象作为排序器。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo05Lambda {
public static Comparator<String> getComparator(){
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o2.length() - o1.length();
// }
// };
return (o1, o2) -> o2.length() - o1.length();
}
public static void main(String[] args){
//创建一个字符串数组
String[] arr = {"a", "bbb", "cc", "dddd"};
//输出排序前的数组 [a, bbb, cc, dddd]
System.out.println(Arrays.toString(arr));
//调用Arrays中的sort方法,对字符串数组进行排序
Arrays.sort(arr, getComparator());
//输出排序后的数组 [dddd, bbb, cc, a]
System.out.println(Arrays.toString(arr));
}
}
3、常用的函数式接口
3.1、Supplier
java.util.function.Supplier接口仅包含一个无参的方法,:T get()。用来获取一个泛型参数执行类型的数据。该接口被称为生产型接口,指定接口的泛型是什么类型,get方法就会生产什么类型的数据。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo06Lambda {
//定义一个方法,方法的参数传递Supplier<T>接口,泛型指定String,get方法就会返回一个String
public static String getString(Supplier<String> supplier){
return supplier.get();
}
public static void main(String[] args) {
String s = getString(()-> "赵丽颖");
System.out.println(s);
}
}
3.1.2、求数组元素最大值案例
使用Supplier接口作为方法参数类型,通过lambda表达式获取数组中的最大值。
arduino
体验AI代码助手
代码解读
复制代码
public class Demo07Lambda {
//定义一个方法,获取int类型数组中元素最大值,方法的参数传递Supplier接口,泛型使用Integer
public static int getMax(Supplier<Integer> supplier) {
return supplier.get();
}
public static void main(String[] args) {
int[] arr = {1, 4, 211, 6, 8, 11, 111};
int maxValue = getMax(()->{
int max = arr[0];
for(int i = 1; i< arr.length; i ++){
if(arr[i]> max){
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
}
}
3.2、Consumer
java.util.function.Consumer接口与Supplier接口相反,它消费数据,数据类型由泛型执行,接口中只有一个抽象方法accept(T t),表示消费一个指定泛型的数据。 定义一个方法,参数传递Consumer接口,泛型使用String,Consumer接口进行消费字符串。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo08Lambda {
public static void consumerStr(String name, Consumer<String> consumer) {
consumer.accept(name);
}
public static void main(String[] args) {
//调用consumerStr方法,传递字符串姓名,方法的另一个参数是一个函数式接口,可以传递lambda表达式
consumerStr("古天乐", name -> {
//消费方式:把字符串进行反转
String revName = new StringBuffer(name).reverse().toString();
System.out.println(revName);
});
}
}
3.2.1、默认方法andThen
可以把两个Consumer接口组合到一起,再对数据进行消费。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo09Lambda {
public static void consumerStr(String s, Consumer<String> con1, Consumer<String> con2){
//con1.accept(s);
//con2.accept(s);
//使用andThen方法,把两个Consumer接口连接到一起再消费
con1.andThen(con2).accept(s);//con1连接con2,先执行con1消费数据,再执行con2消费数据
}
public static void main(String[] args) {
consumerStr("Test", s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toUpperCase()));
}
}
3.2.1.1、格式化打印案例
字符串数组中存在多条信息,按照"姓名":xx,"性别":xx,打印数据。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo10Lambda {
public static void formatString(String s, Consumer<String> con1, Consumer<String> con2){
con1.andThen(con2).accept(s);
}
public static void main(String[] args){
String[] arrs = {"张三,男","李四,男", "王五,女"};
for (String arr: arrs){
formatString(arr, s->{
String name = s.split(",")[0];
System.out.print("姓名:"+name);
}, s->{
String sex = s.split(",")[1];
System.out.print(",性别:"+sex);
});
System.out.println();
}
}
}
3.3、Predicate
对某种数据类型的数据进行判断,结果返回一个boolean值,该接口包含一个抽象方法:test(T t),用来对指定数据类型的数据进行判断,符合条件返回true;不符合条件,返回false。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo11Lambda {
public static boolean checkString(String s, Predicate<String> predicate){
return predicate.test(s);
}
public static void main(String[] args) {
String s = "abcdfges";
boolean isGreaterThanFive = checkString(s, str -> str.length()>5);
System.out.println(isGreaterThanFive);
}
}
3.3.1、默认方法and
Predicate接口中有一个方法and,表示并且关系,用于连接两个判断条件,两个判断条件均为true时,记结果为true。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo12Lambda {
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
return pre1.and(pre2).test(s);
}
public static void main(String[] args) {
String s = "abcdewesdf";
boolean isGreaterThanFiveAndContainsA = checkString(s, str->s.length() > 5, str-> s.contains("a"));
System.out.println(isGreaterThanFiveAndContainsA);
}
}
3.3.2、默认方法or
Predicate接口中有一个方法and,表示并且关系,用于连接两个判断条件,两个判断条件有一个为true时,记结果为true。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo12Lambda {
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
return pre1.or(pre2).test(s);
}
public static void main(String[] args) {
String s = "aaa";
boolean isGreaterThanFiveAndContainsA = checkString(s, str->s.length() > 5, str-> s.contains("a"));
System.out.println(isGreaterThanFiveAndContainsA);
}
}
4、Function
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一数据类型的数据,前者称为前置条件,后者称为后置条件,Function接口中最重要的抽象方法为R apply(T t),根据类型T的参数返回R类型的数据。
4.1、将String类型转换为Integer类型
typescript
体验AI代码助手
代码解读
复制代码
public class Demo13Lambda {
public static int changeString2Int(String num, Function<String, Integer> function){
return function.apply(num);
}
public static void main(String[] args) {
int num = changeString2Int("14", str-> Integer.parseInt(str));
System.out.println(num);
}
}
4.2、默认方法andThen
可以把两个Function接口组合到一起,再对数据进行处理。
typescript
体验AI代码助手
代码解读
复制代码
public class Demo14Lambda {
public static void change(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String s1 = fun1.andThen(fun2).apply(s);
System.out.println(s1);
}
public static void main(String[] args) {
change("123", s -> Integer.parseInt(s), num -> num + "");
}
}