常用的函数接口
-
函数式接口的使用
-
接口MyFunctionalInterface
package cn.xiaoge.day20.demo01; public class Demo { public static void show(MyFunctionalInterface myInter) { myInter.method(); } public static void main(String[] args) { // 调用show方法, 方法的参数是一个接口, 所以可以传递接口的实现类对象 show(new MyFunctionalInterfaceImpl()); // 调用show方法, 方法的参数是一个接口, 所以我们可以传递接口的匿名内部类 show(new MyFunctionalInterface() { @Override public void method() { System.out.println("使用匿名内部类重写接口中的方法"); } }); // 调用show方法, 方法的参数是一个函数式接口, 所以我们可以lambda表达式 show(()->{ System.out.println("使用lambda重写接口中的方法"); }); // 使用lambda简化方式 show(()-> System.out.println("使用lambda简化方式重写接口中的方法")); } }
-
类MyFunctionalInterfaceImpl
package cn.xiaoge.day20.demo01; /* @Override注解 检查方法是否为重写方法 是: 编译成功 否: 编译失败 */ public class MyFunctionalInterfaceImpl implements MyFunctionalInterface{ @Override public void method() { System.out.println("实现类重写接口中的方法"); } }
-
demo
package cn.xiaoge.day20.demo01; public class Demo { public static void show(MyFunctionalInterface myInter) { myInter.method(); } public static void main(String[] args) { // 调用show方法, 方法的参数是一个接口, 所以可以传递接口的实现类对象 show(new MyFunctionalInterfaceImpl()); // 调用show方法, 方法的参数是一个接口, 所以我们可以传递接口的匿名内部类 show(new MyFunctionalInterface() { @Override public void method() { System.out.println("使用匿名内部类重写接口中的方法"); } }); // 调用show方法, 方法的参数是一个函数式接口, 所以我们可以lambda表达式 show(()->{ System.out.println("使用lambda重写接口中的方法"); }); // 使用lambda简化方式 show(()-> System.out.println("使用lambda简化方式重写接口中的方法")); } } // 运行结果 实现类重写接口中的方法 使用匿名内部类重写接口中的方法 使用lambda重写接口中的方法 使用lambda简化方式重写接口中的方法
-
-
性能浪费日志案例&使用Lambda优化日志案例
-
接口MessageBuilder
package cn.xiaoge.day20.demo02; @FunctionalInterface public interface MessageBuilder { // 定义一个拼接消息的抽象方法, 返回被拼接的消息 public abstract String builderMessage(); }
-
Demo01Logger
package cn.xiaoge.day20.demo02; /* 日志案例 发现以下代码存在的一个性能浪费问题 调用showLog方法, 传递的第二个参数是一个拼接后的字符串 先把字符串拼接好, 然后在调用showLog方法 showLog方法中如果传递的日志登记不是1级 那么就不会打印拼接后的字符串 所以感觉字符串白拼接了, 存在浪费 */ public class Demo01Logger { public static void showLog(int level, String msg){ if (level == 1) { System.out.println(msg); } } public static void main(String[] args) { // 定义三个日志信息 String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; // 调用showLog方法, 传递日志级别和日志信息 showLog(1, msg1+msg2+msg3); } } // 运行结果 HelloWorldJava
-
Demo02Lambda
package cn.xiaoge.day20.demo02; /* 使用Lambda优化日志案例 Lambda的特点: 延迟加载 Lambda的使用前提, 必须存在函数式接口 */ import java.io.File; public class Demo02Lambda { // 定义一个现实日志的方法, 方法的参数传递日志的登记和MessageBuilder接口 public static void showLog(int level, MessageBuilder msgBuild){ if (level == 1){ System.out.println(msgBuild.builderMessage()); } } public static void main(String[] args) { // 定义三个日志信息 String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; // 调用showLog方法, 参数MessageBuilder是一个函数式接口, 所以传递Lambda表达式 showLog(1, ()->{ // 返回拼接的字符串 return msg1 + msg2 + msg3; }); /* 使用Lambda表达式作为参数传递, 仅仅是吧参数传递到showLog方法中 只有满足条件, 日志的等级是1级 才会调用接口MessageBuilder中的方法builderMessage 才会进行字符串的拼接 如果条件不满足, 日志的等级不是1级 那么MessageBuilder接口中的方法builderMessage也不会执行 所以拼接字符串的代码也不会执行 所以不会存在性能浪费 */ } } // 运行结果 HelloWorldJava
-
-
函数式接口作为方法参数案例
-
Demo01Runnable
package cn.xiaoge.day20.demo03; /* 例如 java.lang.Runnable 接口就是一个函数式接口, 假设有一个 startThread 方法使用该接口作为参数,那么就 可以使用Lambda进行传参。 这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。 */ public class Demo01Runnable { // 定义一个方法startThread, 方法的参数使用函数是接口Runnable public static void startThread(Runnable runnable){ new Thread(runnable).start(); } public static void main(String[] args) { // 调用startThread方法, 方法的参数是一个接口, 那么我们可以传递这个接口的匿名内部类 startThread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "--->" + "线程启动"); } }); // 代用startThread方法, 方法的参数是一个函数是接口, 所以可以传递Lambda表达式 startThread(()->{ System.out.println(Thread.currentThread().getName() + "--->" + "我是Lambda线程"); }); // 优化Lambda startThread(()->System.out.println(Thread.currentThread().getName() + "--->" + "我是Lambda线程")); } } // 运行结果 Thread-0--->线程启动 Thread-1--->我是Lambda线程 Thread-2--->我是Lambda线程
-
-
函数是接口作为方法的返回值
-
Demo02Comparator
package cn.xiaoge.day20.demo03; /* 如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。 当需要通过一 个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。 */ import java.util.Arrays; import java.util.Comparator; public class Demo02Comparator { // 定义一个方法, 方法的返回值类型使用函数式接口Comparator public static Comparator<String> getComparator(){ // 方法的返回值类型是一个接口, 那么我们可以返回这个接口的匿名内部类 /*return new Comparator<String>() { @Override public int compare(String o1, String o2) { // 按照字符串的降序排序 return o2.length() - o1.length(); } };*/ // 方法的返回值是一个函数式接口, 所以我们可以使用一个Lambda表达式 /*return (String o1, String o2)-> { // 按照字符串的降序排序 return o2.length() - o1.length(); };*/ // 优化Lambda表达式, 定义排序规则 return (o1, o2)->o2.length() - o1.length(); } public static void main(String[] args) { // 创建一个字符串数组 String[] arr = {"aaa", "b", "cccc", "dddddd"}; // 输出排序前的数组 System.out.println(Arrays.toString(arr)); // 调用Arrays中的sort方法, 对字符串进行排序 Arrays.sort(arr, getComparator()); System.out.println(Arrays.toString(arr)); } } // 运行结果 [aaa, b, cccc, dddddd] [dddddd, cccc, aaa, b]
-
-
常用的函数式接口_Supplier接口
-
Demo01Supplier
package cn.xiaoge.day20.demo04; /* 常用的函数式接口 java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对 象数据。 Supplier(T)接口称之为生产型接口, 指定接口的泛型是什么类型, 那么接口中的get方法就会生产什么类型的数据 */ import java.util.function.Supplier; public class Demo01Supplier { // 定义一个方法, 方法的参数传递Supplier<T>接口, 泛型执行String, get方法就会返回一个String public static String getString(Supplier<String> s){ return s.get(); } public static void main(String[] args) { // 调用getString方法, 方法的参数Supplier是一个函数式接口, 所以可以传递Lambda表达式 System.out.println(getString(()-> { // 生产一个字符串, 并返回 return "hello world"; })); // 优化lambda表达式 System.out.println(getString(()-> "hello world")); } } // 运行结果 hello world hello world
-
Demo02Test
package cn.xiaoge.day20.demo04; /* 练习: 求数组元素最大值 使用Supplier接口作为方法的参数类型, 通过lambda表达式求出int数组中的最大值. 提示: 接口的泛型请使用java.lang.Integer类. */ import java.util.function.Supplier; public class Demo02Test { // 定义一个方法, 用于获取int类型数组中元素的最大值, 方法的参数传递Supplier接口, 泛型使用Integer public static int getMax(Supplier<Integer> sup) { return sup.get(); } public static void main(String[] args) { // 定义一个int类型的数组, 并赋值 int[] arr = {2, 4, 1, 6, 3, 7, 5}; // 调用getMax方法, 方法的参数Supplier是一个函数式接口, 所以可以传递Lambda表达式 int maxValue = getMax(()->{ // 获取数组的最大值, 并返回 // 定义一个变量, 把数组中的第一个元素赋值给该变量, 记录数组中元素的最大值 int max = arr[0]; // 最大值 // 遍历数组, 获取数组中的最大值 for(int i=1; i < arr.length; i++){ // 使用其他的元素和最大值比较 if (arr[i] > max) { // 如果arr[i]大于max, 则替换max作为最大值 max = arr[i]; } } // 返回最大值 return max; }); System.out.println("数组中元素的最大值: " + maxValue); } } // 运行结果 数组中元素的最大值: 7
-
-
常用的函数式接口_Consumer
-
Demo01Consumer
package cn.xiaoge.day20.demo05; /* java.util.function.Consumer<T>接口则正好与Supplier接口相反 它不是生产一个数据, 而是消费一个数据, 其数据类型由泛型决定. Consumer接口中包含抽象方法void accept(T t), 意为消费一个指定泛型的数据 Consumer接口是一个消费型接口, 它的泛型指定什么类型, 就可以使用accept方法消费什么类型的数据 至于具体怎么消费(使用), 需要自定义(输出, 计算....) */ import java.util.Arrays; import java.util.function.Consumer; public class Demo01Consumer { /* 定义一个方法 方法的参数传递一个字符串的姓名 方法的参数传递Consumer接口, 泛型使用String 可以使用Consumer接口消费字符串姓名 */ public static void method(String name, Consumer<String> con) { con.accept(name); } public static void main(String[] args) { // 调用method方法, 传递字符串姓名, 方法的另一个参数是Consumer接口, 是一个函数式接口, 所以可以传递Lambda表达式 method("金泫雅", (String name)->{ // 对传递的字符串进行消费 // 消费方式, 直接输出字符串 System.out.println(name); // 消费方式, 把字符串进行反转输出 // StringBuffer(name) 字符串缓冲区的内容就是该字符串值 // reverse反转数组值, 返回字符数组 // 重写了toString方法, 把字符数组转换为字符串 String reName = new StringBuffer(name).reverse().toString(); System.out.println(reName); }); } } // 运行结果 金泫雅 雅泫金
-
Demo02AndThen
package cn.xiaoge.day20.demo05; /* Consumer接口的默认方法andThen 作用: 需要两个Consumer接口, 可以把两个Consumer接口组合到一起, 在对数据进行消费 列如: Consumer<String> con1 Consumer<String> con2 String s = "hello"; con1.accept(s); con2.accept(s); 等同于 链接两个Consumer接口 在进行消费 con1.andThen(con2).accept(s); 谁写前边谁先消费 */ import java.util.function.Consumer; public class Demo02AndThen { // 定义一个方法, 方法的参数传递一个字符串和两个Consumer接口, Consumer接口的泛型使用字符串 public static void method(String s, Consumer<String> con1, Consumer<String> con2) { // 使用andThen方法, 把两个Consumer接口链接到一起, 在消费数据 con1.andThen(con2).accept(s); // con1链接con2, 先执行con1消费数据, 在执行con2消费数据 } public static void main(String[] args) { method("Hello", (String s1)->{ // 把字符串转换为大写输出 System.out.println(s1.toUpperCase()); }, (String s2)->{ // 把字符串转换为小写输出 System.out.println(s2.toLowerCase()); }); } } // 运行结果 HELLO hello
-
Demo03Test
package cn.xiaoge.day20.demo05; /* 练习: 字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。 要求将打印姓 名的动作作为第一个 Consumer 接口的Lambda实例, 将打印性别的动作作为第二个 Consumer 接口的Lambda实 例, 将两个 Consumer 接口按照顺序“拼接”到一起 */ import java.util.function.Consumer; public class Demo03Test { // 定义一个方法, 参数传递String类型的数组和两个Consumer接口, 泛型使用String public static void method(String[] arr, Consumer<String> con1, Consumer<String> con2){ for (String s : arr) { // 使用呢andThen方法链接两个Consumer接口, 消费字符串 con1.andThen(con2).accept(s); } } public static void main(String[] args) { String[] arr = {"金泫雅,女", "金所炫,女", "权志龙,男"}; method(arr, (String s1)->{ String name = s1.split(",")[0]; System.out.print("姓名: " + name); }, (String s2)->{ String gender = s2.split(",")[1]; System.out.println(".年龄: " + gender + ". "); }); } } // 运行结果 姓名: 金泫雅.年龄: 女. 姓名: 金所炫.年龄: 女. 姓名: 权志龙.年龄: 男.
-
-
常用的函数式接口_Predicate
-
Demo01Predicate
package cn.xiaoge.day20.demo06; /* java.util.function.Predicate<T>接口 作用: 对某种数据类型的数据进行判断, 结果返回一个boolean值 Predicate接口中包含一个抽象方法: boolean test(T t): 用来对指定数据类型数据进行判断的方法 结果: 符合条件, 返回true 不符合条件, 返回false */ import java.util.function.Predicate; public class Demo01Predicate { /* 定义一个方法 参数传递一个String类型的字符串 传递一个Predicate接口, 泛型使用String 使用Predicate中的方法test对字符串进行判断, 并把判断的结果返回 */ public static boolean checkString(String s, Predicate<String> p) { return p.test(s); } public static void main(String[] args) { // 定义一个字符串 String s = "abcdef"; // 调用checkString方法对字符串进行校验, 参数传递字符串和Lambda表达式 /*boolean isTrue = checkString(s, (String str)->{ return s.length() > 5; });*/ // 优化Lambda boolean isTrue = checkString(s, (str)->s.length() > 5); System.out.println(isTrue); } } // 运行结果 true
-
Demo02Predicate_and
package cn.xiaoge.day20.demo06; /* 逻辑表达式: 可以链接多个判断的条件 &&: 与运算符, 有false则false ||: 或运算符, 有true则true !: 非(取反)运算符, 非真则假, 非假则真 需求: 判断一个字符串, 有两个判断的条件 1. 判断字符串的长度是否大于5 2. 判断字符串中是否包含a 两个条件必须同时满足, 我们就可以使用&&运算符链接两个条件 Predicate接口中有一个方法and, 表示并且关系, 也可以用于连接诶两个判断条件 default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> this.test(t) && other.test(t); } 方法内部的两个判断条件, 也是使用&&运算符链接起来的. */ import java.util.function.Predicate; public class Demo02Predicate_and { /* 定义一个方法, 方法的参数, 传递一个字符串 传递两个Predicate接口 一个用于判断字符串的长度是否大于5 一个用于判断字符串中是否包含a 两个条件必须同时满足 */ public static boolean method(String s, Predicate<String> p1, Predicate<String> p2) { // return p1.test(s) && p2.test(s); return p1.and(p2).test(s); // 等价于 return p1.test(s) && p2.test(s); } public static void main(String[] args) { String s = "abcdef"; boolean b = method(s, (String s1)->{ return s1.length() > 5; }, (String s2)->{ // contains判断字符串是否包含a return s2.contains("a"); }); System.out.println(b); } } // 运行结果 true
-
Demo03Predicate_or
package cn.xiaoge.day20.demo06; /* 逻辑表达式: 可以链接多个判断的条件 &&: 与运算符, 有false则false ||: 或运算符, 有true则true !: 非(取反)运算符, 非真则假, 非假则真 需求: 判断一个字符串, 有两个判断的条件 1. 判断字符串的长度是否大于5 2. 判断字符串中是否包含a 满足一个条件, 我们就可以使用||运算符链接两个条件 Predicate接口中有一个方法or, 表示或者关系, 也可以用于连接诶两个判断条件 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> this.test(t) || other.test(t); } 方法内部的两个判断条件, 也是使用||运算符链接起来的. */ import java.util.function.Predicate; public class Demo03Predicate_or { /* 定义一个方法, 方法的参数, 传递一个字符串 传递两个Predicate接口 一个用于判断字符串的长度是否大于5 一个用于判断字符串中是否包含a 满足一个条件即可 */ public static boolean method(String s, Predicate<String> p1, Predicate<String> p2) { // return p1.test(s) || p2.test(s); return p1.or(p2).test(s); // 等价于 return p1.test(s) || p2.test(s); } public static void main(String[] args) { String s = "abcde"; boolean b = method(s, (String s1)->{ return s1.length() > 5; }, (String s2)->{ // contains判断字符串是否包含a return s2.contains("a"); }); System.out.println(b); } } // 运行结果 true
-
Demo04Predicate_negate
package cn.xiaoge.day20.demo06; /* 需求: 判断一个字符串长度是否大于5 如果字符串的长度大于5, 那么返回false 如果字符串的长度不大于5, 那么返回true 所以我们可以使用取反符号!对判断的结果进行取反 Predicate接口中有一个方法negate, 也表示取反的意思 default Predicate<T> negate() { return (t) -> !test(t); } */ import java.util.function.Predicate; public class Demo04Predicate_negate { /* 定义一个方法, 方法的参数, 传递一个字符串 使用Predicate接口判断字符串的长度是否大于5 */ public static boolean method(String s, Predicate<String> p) { return p.negate().test(s); } public static void main(String[] args) { boolean b = method("123456", (String s)->{ // 判断字符串的长度是否大于5, 并且返回结果 return s.length()>5; }); System.out.println(b); } } // 运行结果 false
-
Demo05Test
package cn.xiaoge.day20.demo06; /* 练习: 集合信息筛选 数组当中有多条“姓名+性别”的信息如下, String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" }; 请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 ArrayList 中, 需要同时满足两个条件: 1. 必须为女生; 2. 姓名为4个字。 分析: 1. 有两个判断条件, 所以需要使用两个Predicate接口, 对条件进行判断 2. 必须同时满足两个条件, 所以使用and方法链接两个判断 */ import java.util.ArrayList; import java.util.function.Predicate; public class Demo05Test { /* 定义一个方法 方法的参数传递一个包含人员信息的数组 传递两个Predicate接口, 用于对数组中的信息进行过滤 把满足条件的信息存到ArrayList集合中并返回 */ public static ArrayList<String> filter(String[] arr, Predicate<String> p1, Predicate<String> p2) { // 定义一个ArrayList集合, 存储过滤之后的信息 ArrayList<String> list = new ArrayList<>(); // 遍历数组, 获取数组中的每一条信息 for (String s : arr) { // 使用Predicate接口中的方法test多获取到的字符串进行判断 boolean b = p1.and(p2).test(s); if(b){ // 条件成立, 两个条件都满足, 把信息存储到ArrayList集合中 list.add(s); } } // 把集合返回 return list; } public static void main(String[] args) { // 定义一个存储字符串的数组 String[] arr = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女"}; // 调用filter方法, 传递字符串数组和两个Lambda表达式 ArrayList<String> list = (filter(arr, (String s1)->{ String name = s1.split(",")[0]; // 判断名字长度是否等于4 return name.length() == 4; }, (String s2)->{ String gender = s2.split(",")[1]; // 判断性别是否为女 return "女".equals(gender); })); System.out.println(list); } } // 运行结果 [迪丽热巴,女, 古力娜扎,女]
-
-
常用的函数式接口_Function
-
Demo01Function
package cn.xiaoge.day20.demo07; /* java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据, 前者称为前置条件, 后者称为后置条件。 Function 接口中最主要的抽象方法为: R apply(T t) ,根据类型T的参数获取类型R的结果。 使用的场景例如:将 String 类型转换为 Integer 类型。 */ import java.util.function.Function; public class Demo01Function { /* 顶一个方法 方法的参数传递一个字符串类型的整数 方法的参数列表传递一个Function接口, 泛型使用<String, Integer> 使用Function接口中的方法apply, 把字符串类型的整数, 转换为Integer类型的整数 */ public static void change(String s, Function<String, Integer> f) { // Integer i = f.apply(s); int i = f.apply(s); // 自动拆箱 System.out.println(i); } public static void main(String[] args) { // 调用change方法, 传递字符串类型的整数, 和Lambda表达式 change("123456", (String s)->{ // 把字符串类型的整数, 转换为Integer类型的整数返回 return Integer.parseInt(s); }); // 优化Lambda表达式 change("123456", (String s)->Integer.parseInt(s)); } } // 运行结果 123456 123456
-
Demo02Function_andThen
package cn.xiaoge.day20.demo07; /* Function接口中的默认方法andThen: 用来进行组合操作 需求: 把String类型的"123", 转换为Integer类型, 把转换后的结果加10 把增加之后的Integer类型的数据, 转换为String类型 分析: 转换了两次 第一次是把String类型转换为了Integer类型 所以我们可以使用Function<String, Integer> f1 Integer num = f1.apply("123") + 10; 第二次是把Integer类型转换为String类型 所以我们可以使用Function<Integer, String> f2 String s = f2.apply(num); 我们可以使用andThen方法, 把两次转换组合在一起使用 String s = f1.andThen(f2).apply("123"); f1先调用apply方法, 把字符串转换为Integer f2在调用apply方法, 把Integer转换为字符串 */ import java.util.function.Function; public class Demo02Function_andThen { public static void change(String s, Function<String, Integer> f1, Function<Integer, String> f2) { System.out.println(f1.andThen(f2).apply(s)); } public static void main(String[] args) { change("123", (String s)->{ // 将字符串转换为Integer类型 int num = Integer.parseInt(s); // 自动拆箱 return num+10; }, (Integer num)->{ // 把Integer类型转换为字符串 也可以直接 + ""转换为字符串 String s = Integer.toString(num); // getClass获取变量类型 System.out.println(s.getClass()); return s; }); // 优化Lambda表达式 change("123", (s)->Integer.parseInt(s)+10, (num)->Integer.toString(num)); } } // 运行结果 class java.lang.String 133 133
-
Demo03Test
package cn.xiaoge.day20.demo07; /* 练习:自定义函数模型拼接 题目 请使用 Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为: String str = "赵丽颖,20"; 分析: 1. 将字符串截取数字年龄部分,得到字符串; Function<String, String> "赵丽颖,20" --> "20" 2. 将上一步的字符串转换成为int类型的数字; Function<String, Integer> "20" --> 20 3. 将上一步的int数字累加100,得到结果int数字。 Function<Integer, Integer> 20 --> 120 */ import java.util.function.Function; public class Demo03Test { /* 定义一个方法 参数传递包含姓名和年龄的字符串 参数在传递3个Function接口用于类型转换 */ public static int change(String s, Function<String, String> f1, Function<String, Integer> f2, Function<Integer, Integer> f3) { // 使用andThen方法把三个转换组合到一起 return f1.andThen(f2).andThen(f3).apply(s); } public static void main(String[] args) { // 调用change方法, 参数传递字符串和3个Lambda表达式 int num = change("赵丽颖,20", (String s1)->{ // "赵丽颖,20" --> "20" return s1.split(",")[1]; }, (String s2)->{ // "20" --> 20 return Integer.parseInt(s2); }, (Integer i)->{ // 20 --> 120 return i+100; }); System.out.println(num); // 优化Lambda表达式 int num2 = change("赵丽颖,20", (s1)->s1.split(",")[1], (s2)->Integer.parseInt(s2), (i)->i+100); System.out.println(num2); } } // 运行结果 120 120
-