Java学习-函数式接口
- 1.函数式接口
- 1.1概述
- 1.2语法
- 1.3@FunctionalInterface
- 2. 函数式编程
- 2.1Lambda的延迟执行
- 2.2 使用Lambda作为参数和返回值
- Lambda作为参数
- Lambda作为返回值
- 3.Java常用函数式接口
- 3.1 Supplier接口
- 3.2 Consumer接口
- 3.3 Predicate接口
- 3.4 Function接口
1.函数式接口
1.1概述
函数式接口在Java中是指:有且仅有一个抽象方法的接口。 函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
当然接口中可以包含其它的方法(默认,静态,私有)
1.2语法
只要确保接口中有且仅有一个抽象方法即可:
修饰符 interface 接口名称 {
public abstract 返回值类型 方法名称(可选参数信息);
// 其他非抽象方法内容
}
1.3@FunctionalInterface
当我们定义一个函数式接口的时候,我们可以使用注解 @FunctionalInterface 来注释
@FunctionalInterface:
检测是否是函数时接口
- 如果接口有且只有一个抽象方法,编译成功
- 当没有抽象方法,或则多余一个抽象方法的时候,会编译不通过
@FunctionalInterface
public interface MyFunctionInterface {
public abstract void show();
}
2. 函数式编程
2.1Lambda的延迟执行
- 性能浪费的日志案例
/*
性能浪费日志案例:
日志可以帮助我们快速定位问题,记录程序运行过程的情况,以便项目的监控和优化
一种典型的场景就是对参数进行有条件的使用,例如对日志的消息凭借后,满足条件情况下打印日志
*/
public class FuncDemo {
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, msgA + msgB + msgC);
/*
无论级别是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方
法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。
*/
//使用Lambda表达式实现性能的优化,当不满足条件的时候,字符串拼接操作是不会执行的
log2(1, () -> msgA + msgB + msgC);
}
private static void log(int level, String msg) {
if (level == 1) {
System.out.println(msg);
}
}
private static void log2(int level, MessageBuilder messageBuilder) {
if (level == 1) {
System.out.println(messageBuilder.buildMessage());
}
}
}
2.2 使用Lambda作为参数和返回值
Lambda作为参数
//首先定义一个函数式接口
@FunctionalInterface
public interface MyFunctionInterface {
public abstract void show();
}
//定义一个方法
public static void showMethod(MyFunctionInterface myFunctionInterface){
myFunctionInterface.show();
}
//测试代码
//接口的作为参数的实现方式
//1.直接传递实现接口的实现类
showMethod(new MyFunctionInterfaceImpl());
//2.使用匿名内部类的方式来实现
showMethod(new MyFunctionInterface() {
@Override
public void show() {
System.out.println("匿名内部类的方式来实现接口");
}
});
//3.使用Lambda的方式来实现接口(只能函数式接口)
showMethod(() -> {
System.out.println("使用Lambda表达式来实现函数式接口");
});
//4.Lambda表达式的简化实现
showMethod(() -> System.out.println("使用Lambda的简化方式来实现函数式接口"));
Lambda作为返回值
/*
定义一个使用Lambda表达式作为返回参数的方法
*/
public static Comparator<String> customComparator() {
//1.使用匿名内部类的方式来返回一个比较器
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length() - o2.length();
// }
// };
//2.使用Lambda表示的方式返回
// return (String o1, String o2) -> {
// return o1.length() - o2.length();
// };
//3.使用Lambda的简化方式
return (String o1, String o2) -> o1.length() - o2.length();
}
//Lambda表达式作为返回值的测试
String[] temp = {"aaaa","b","cccccccc","dddddddddd"};
System.out.println("没有排序前的字符串数组=====" + Arrays.toString(temp
));
Arrays.sort(temp, customComparator());
System.out.println("排序后的字符串数组=======" + Arrays.toString(temp
));
3.Java常用函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。 下面是最简单的几个接口及使用示例。
3.1 Supplier接口
java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对 象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象 数据。
- 代码示例:
public class SupplierDemo {
public static void main(String[] args) {
//测试Supplier的简单用法
supplierTest();
supplierExample();
}
/*
求数组中元素的最大值
*/
public static void supplierExample(){
int[] arr = {100,80,120,60};
int maxCount = getMax(arr, () ->{
int max = arr[0];
for (int i = 1; i < arr.length-1; i++) {
if (max > arr[i]) {
max = arr[i];
}
}
return max;
});
}
public static int getMax(int[] arr, Supplier<Integer> supplier) {
return supplier.get();
}
/*
supplier 的简单运用代码
*/
public static void supplierTest() {
/*
Supplier: java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get()
用来获取一个泛型参数指定类型的对 象数据,生产数据
*/
//简单使用-> 使用匿名内部类的方式
int count = getCount(new Supplier<Integer>() {
@Override
public Integer get() {
return 20;
}
});
System.out.println("count======" + count);
//使用Lambda表达式来使用
int count2 = getCount(() -> {
return 30;
});
System.out.println("Lambda表达式====" + count2);
//优化
int count3 = getCount(() -> 60);
System.out.println("优化后lambda表达式======" + count3);
}
/*
使用Supplier接口生产一个Int类型的数据
*/
public static int getCount(Supplier<Integer> supplier) {
return supplier.get();
}
}
3.2 Consumer接口
java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。
- 代码示例
public class ConsumerDemo {
public static void main(String[] args) {
//1.Consumer:接口与Supplier接口正好相反 是消费数据
consumerTest("我爱Java", new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("匿名内部类实现====" + s);
}
});
consumerTest("我爱Java",(String s) -> {
System.out.println("Lambda表达式=====" + s);
});
//优化
consumerTest("我爱Java",s -> System.out.println("Lambda表达式优化之后=====" + s));
//Consumer中的默认方法测试 andThen() 方式
consumerTest2("我爱Java",(String s) -> {
System.out.println("andThen first====" + s);
},(String s) -> {
System.out.println("andThen seconde=====" + s);
});
}
public static void consumerTest(String testString, Consumer<String> consumer) {
consumer.accept(testString);
}
public static void consumerTest2(String testString, Consumer<String> consumer, Consumer<String> consumer2) {
consumer.andThen(consumer2).accept(testString);
}
}
3.3 Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用
java.util.function.Predicate 接口。
*代码案例:
public class PredicateDemo {
public static void main(String[] args) {
//测试判断字符串中包含不包含a
String str = "dsadsad";
boolean b = predicateTest(str, s -> {
return s.contains("a");
});
System.out.println(b);
//判断字符串里面是否同时包含s 还包含b and方法
boolean b1 = predicateTest2(str, s -> s.contains("s"), s -> s.contains("b"));
System.out.println(b1);
//使用or方法来判断
boolean b2 = predicateTest2(str, s -> s.contains("s"), s -> s.contains("d"));
System.out.println(b2);
//negate() 非的结果 结果取反
boolean b3 = predicateTest(str, s -> s.contains("x"));
System.out.println(b3);
}
public static boolean predicateTest(String testString, Predicate<String> predicate) {
return predicate.test(testString);
}
public static boolean predicateTest2(String testString, Predicate<String> predicate, Predicate<String> predicate2) {
return predicate.and(predicate2).test(testString);
}
public static boolean predicateTest3(String testString, Predicate<String> predicate, Predicate<String> predicate2) {
return predicate.or(predicate2).test(testString);
}
}
3.4 Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
- 代码案例:
public class FunctionDemo {
public static void main(String[] args) {
int count = functionTest("123", s -> Integer.parseInt(s));
System.out.println("count======" + count);
//anThen的方法连续转转 谁在前 谁先转换 第一步转换完成之后s编程Integer类型 , 在接着把Integer转换成String类型
String str = functionTest2("123",s -> Integer.parseInt(s), s -> String.valueOf(count));
System.out.println("str====== " + str);
}
/*
Function<String, Integer>: 把String类型的数据转换成为Integer类型的数据 apply()方法
*/
public static int functionTest(String testString, Function<String, Integer> function) {
return function.apply(testString);
}
public static String functionTest2(String testString, Function<String, Integer> function, Function<Integer, String> function2) {
return function.andThen(function2).apply(testString);
}
}