🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 常用的函数式接口 ## 回顾 &nbsp;&nbsp;函数接口的概念在之前的文章《Lambda表达式》中就有提到,函数接口配合Lambda表达式的使用在一些情况下可以大大简化java代码的书写。函数接口指的就是接口中只有一个抽象方法, 并且接口使用@FunctionalInterface注解进行修饰。JDK中帮我们定义好了一些常用的函数接口,这些函数接口配合Lambda和后面要讲的stream流进行一些**过滤数据**的操作,用起来非常简洁。常用在“作为参数传递给一个方法”的场景中。下面就来讲常用到的函数接口有哪些。 &nbsp;&nbsp; ## Supplier接口 &nbsp;&nbsp;(supplier 供应商)是一个"工厂"类型接口,用来"生产"对应的数据类型,不用传入任何数据。具体使用在后面的stream流中可以体现。 ~~~ @FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); } ~~~ ### 抽象方法 > T get():用来获取一个泛型参数类型指定的数据,即用来提供一个符合要求的数据类型,"生产"一个数据,就跟"供应商"一样。 举例:获取一个数组中的最大值 ```java public class SupplierDemo { public static int getMaxValue(Supplier<Integer> supplier) { //接口作为参数进行传递 return supplier.get(); } public static void main(String[] args) { int[] nums = {2, 9, 19, 1, 20}; int max = getMaxValue(()->{ //使用Lambad表达式 int max = nums[0]; for (int i = 1; i < nums.length; i++) { max = nums[i] > max ? nums[i] : max; } return max; }); } } ``` &nbsp;&nbsp;get()方法的目的就是给我们提供一个想要的数据类型。 &nbsp;&nbsp; ## Consumer接口 Consumer接口具有和Supplier接口相反的功能,主要是为了消费一个数据。 ~~~ @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } } ~~~ ### 抽象方法 > void accept(T):对给定的参数执行该方法的内容。 举例:将传递的字符串数据进行逆转 ```java public class ConsumerDemo { public static void reserveString(String str, Consumer<String> con) { con.accept(str); } public static void main(String[] args) { String str = "lambda"; reserveString(str, (str)->{ System.out.println(str); //lambda StringBuffer reStr = new StringBuffer(str); reStr.reverse().toString(); //将字符串进行逆转 System.out.println(reStr); //adbmal }); } } ``` ### 默认方法 与Supplier接口不同的是,Consumer接口中实现了一个默认的方法,addThen() ```java /** * 默认方法 * 1. 返回一个组合的Consumer对象,并且会按照顺序执行accept方法:即可以拼接多个进行执行 * 2. 如果after方法执行出现异常(或接下去拼接的出现异常了)就会将该异常转发给调用者(例如main)。 * 3. 如果执行this操作出现异常时,不会执行after的操作。 */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); //判断是否为空指针 return (T t) -> { accept(t); after.accept(t); }; //这也是一个Consumer接口accept的lambad实现 } ``` 举例:将一个字符串转化为大小写 ```java public class ConsumerDemo { public static void method(String str, Consumer<String> con1, Consumer<String> con2) { con1.andThen(con2).accept(str); } public static void main(String[] args) { String str = "Lambda"; method(str, (t)->{ System.out.println(t.toUpperCase()); //LAMBDA }, (t) -> { System.out.println(t.toLowerCase()); //lambda }); } } ``` 可以使用默认方法实现一个责任链模式。 &nbsp;&nbsp; ## Predicate接口 (Predicate 谓词,离散数学中的概念),传入一个参数,对参数进行逻辑判断,看参数是否符合条件(在给定的参数上评估这个谓词),返回值为boolean。 ```java @FunctionalInterface public interface Predicate<T> { } ``` ### 抽象接口 > boolean test(T t):判断传入的参数是否符合条件 举例:传入一个字符串列表,打印长度大于4的字符串 ```java public class PredicateDemo { public static void checkString(String[] strs, Predicate<String> pre) { for (int i = 0; i < strs.length; i++) { if (pre.test(strs[i])) { System.out.println(strs[i]); } } } public static void main(String[] args) { String[] strs = {"java", "c", "C++", "python", "javascript", "Typescript"}; checkString(strs, (str)->{ return str.length() > 4; }); //python, javascript,Typescript } } ``` ### 默认方法 &nbsp;&nbsp;既然是用来进行逻辑判断的,少不了要用到逻辑运算符,Predicate接口里面就内置了一些默认的逻辑运算方法: ```java /** * 1.返回一个通过逻辑与组合的Predicate接口 * 2.other.test抛出异常的话将该异常转发给调用者 * 3.this.test抛出异常的话不执行other.test的内容 */ default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } //逻辑非 default Predicate<T> negate() { return (t) -> !test(t); } //逻辑或 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } ``` 举例:判断字符串包含a并且长度大于5 ```java public class PredicateDemo { public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2) { return pre1.and(pre2).test(str); } public static void main(String[] args) { String str = "lambda"; boolean b = checkString(str, (str1)->{ return str1.length() > 5; }, (str1) -> { return str1.contains("a"); }); System.out.println(b); // true } } ``` &nbsp;&nbsp; ## Function接口 ```java @FunctionalInterface public interface Function<T, R> {} ``` &nbsp;&nbsp;该接口用来将类型T转化为类型R,T称为前置条件,R称为后置条件 &nbsp;&nbsp; ### 抽象方法 > R apply(T t):将给定参数类型T转化为R类型 例如,将String类型数字转化为Integer类型 ```java public class FunctionDemo { private static void transform(Function<String, Integer> function) { int num = function.apply("10"); //数字10 System.out.println(num + 20); } public static void main(String[] args) { transform(s ‐> Integer.parseInt(s)); } } ``` ### 默认方法 &nbsp;&nbsp;与Consumer类似的,Function也有一个addThen方法,该方法也是返回一个组合操作后的Function接口,最终转化的数据类型与after转化的数据类型一致。 ```java default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); //判断是否为空指针 return (T t) -> after.apply(apply(t)); //这也是一个Consumer接口apply的lambad实现 } ``` 举例:将字符串数字转化为Integer类型后+10再转化为字符串 ```java public class FunctionDemo{ public static void addTen(String str, Function<String, Integer> fun1, Function<Integer, String> fun2) { //注意最后返回的与fun2的类型相同 String s = fun1.andThen(fun2).apply(str); System.out.println(s); //"20" } public static void main(String[] args) { String s = "10"; addTen(s, (String str1) ->{ return Integer.parseInt(str1) + 10; }, (Integer integer) -> { return integer.toString(); }); } } ``` &nbsp;&nbsp; ## 小结 1. 通过函数式接口,我们可以看到lambda表达式的简洁之处。 2. 虽然现阶段我们可能会觉得这么进行操作多此一举,但是这些接口在后面的stream流就有很大的用处。