文章目录
- 函数式接口
- 一、函数式接口
- 1. **概念**
- 2. 函数式接口的使用
- 二、函数式编程
- 1. Lambda的延迟执行
- 2. 性能浪费的日志案例
- 3. 使用Lambda表达式优化日志案例
- 三、常用函数式接口
- 1. Supplier接口
- 2. Consumer接口
- 3. Predicate接口
- 4. Function接口
函数式接口
一、函数式接口
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;
});
}
}