函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可
以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实
底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部
类的“语法糖”,但是二者在原理上是不同的。
@FunctionalInterface注解
@FunctionalInterface public interface ParentInterface { String method(); }
性能浪费的日志案例
注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。
一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:
1 package com.lamda; 2 3 import org.junit.Test; 4 5 import java.util.*; 6 import java.util.function.Consumer; 7 import java.util.function.Function; 8 import java.util.function.Predicate; 9 import java.util.function.Supplier; 10 11 public class DemoChild { 12 /** 13 * 自定义函数式编程 14 */ 15 @Test 16 public void test01() { 17 doSomeThing(() -> { 18 System.out.println("1111"); 19 return ""; 20 }); 21 } 22 23 private static void doSomeThing(ParentInterface child) { 24 child.method(); 25 } 26 27 //延迟加载 28 @Test 29 public void test02() { 30 String msgA = "a"; 31 String msgB = "ab"; 32 String msgC = "acb"; 33 log(1, () -> { 34 return msgA + msgB + msgC; 35 }); 36 } 37 38 public void log(int level, ParentInterface p) { 39 if (level == 1) { 40 System.out.println(p.method()); 41 } 42 } 43 44 //作为参数 45 @Test 46 public void test03() { 47 startThread(() -> { 48 System.out.println("abc"); 49 }); 50 } 51 52 public static void startThread(Runnable task) { 53 new Thread(task).start(); 54 } 55 56 //返回值 57 private static Comparator<String> newComparator() { 58 return (a, b) -> b.length() - a.length(); 59 } 60 61 @Test 62 public void test04() { 63 String[] array = {"abc", "bcd", "bbcd"}; 64 Arrays.sort(array, newComparator()); 65 System.out.println(Arrays.toString(array)); 66 } 67 68 //另外一种排序写法 69 private void sortOrder() { 70 ArrayList<String> list = new ArrayList<String>(); 71 list.add("cba"); 72 list.add("aba"); 73 list.add("sba"); 74 list.add("nba"); 75 //排序方法 按照第一个单词的降序 76 Collections.sort(list, new Comparator<String>() { 77 @Override 78 public int compare(String o1, String o2) { 79 return o2.charAt(0) - o1.charAt(0); 80 } 81 }); 82 } 83 84 /** 85 * java.util.function.Supplier<T> 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对 86 * 象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象 87 * 数据。 88 * 打印结果 Helloworld 89 */ 90 @Test 91 public void test05() { 92 String msgA = "Hello"; 93 String msgB = "world"; 94 System.out.println(get(() -> msgA + msgB)); 95 } 96 97 public static String get(Supplier<String> p) { 98 return p.get(); 99 } 100 101 //案例求最大值 102 @Test 103 public void test06() { 104 int[] array = {2, 183, 1, 9}; 105 Integer max = getMax(() -> { 106 int num = array[0]; 107 for (int i : array) { 108 if (i > num) 109 num = i; 110 } 111 return num; 112 113 }); 114 System.out.println(max); 115 } 116 117 public static Integer getMax(Supplier<Integer> p) { 118 return p.get(); 119 } 120 121 /** 122 * java.util.function.Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 123 * 其数据类型由泛型决定。 124 */ 125 @Test 126 public void test7() { 127 consumeStr(s -> System.out.println(s.toUpperCase()), 128 s -> System.out.println(s.toLowerCase()) 129 ); 130 } 131 132 public void consumeStr(Consumer<String> s, Consumer<String> b) { 133 s.andThen(b).accept("zhaNgSAN"); 134 } 135 136 //demo,打印数组 137 public static void printInfo(Consumer<String> a, Consumer<String> b, String[] arr) { 138 for (String info : arr) { 139 a.andThen(b).accept(info); 140 } 141 } 142 143 @Test 144 public void test8() { 145 String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"}; 146 printInfo(s -> System.out.println(s.split(",")[0]), 147 s -> System.out.println(s.split(",")[1]), 148 array 149 ); 150 } 151 152 /** 153 * 有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 154 * java.util.function.Predicate<T> 接口。 155 */ 156 @Test 157 public void test9() { 158 demo9(s -> s.length() > 5); 159 } 160 161 private void demo9(Predicate<String> p) { 162 boolean helloWorld = p.test("HelloWorld"); 163 System.out.println(helloWorld); 164 } 165 166 /** 167 * 既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 168 * 现“并且”的效果时,可以使用default方法 and 。其JDK源码为: 169 */ 170 @Test 171 public void test10() { 172 demo10(s -> s.contains("H"), d -> d.contains("D")); 173 } 174 175 private void demo10(Predicate<String> p, Predicate<String> b) { 176 boolean helloWorld = p.and(b).test("HelloWorld"); 177 System.out.println(helloWorld); 178 } 179 180 /** 181 * 既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 182 * 现“并且”的效果时,可以使用default方法 and 。其JDK源码为: 183 */ 184 @Test 185 public void test11() { 186 demo11(a -> a.contains("H"), 187 b -> b.contains("e") 188 ); 189 } 190 191 private void demo11(Predicate<String> p, Predicate<String> b) { 192 boolean helloWorld = p.or(b).or(b).test("HelloWorld"); 193 System.out.println(helloWorld); 194 } 195 196 /** 197 * 既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 198 * 现“并且”的效果时,可以使用default方法 and 。其JDK源码为: 199 */ 200 @Test 201 public void test12() { 202 demo12(a -> a.contains("b"), 203 b -> b.length() > 11 204 ); 205 } 206 207 private void demo12(Predicate<String> p, Predicate<String> b) { 208 boolean helloWorld = p.negate().test("HelloWorld"); 209 System.out.println(helloWorld); 210 } 211 212 /** 213 * 既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 214 * 现“并且”的效果时,可以使用default方法 and 。其JDK源码为: 215 */ 216 @Test 217 public void test13() { 218 demo13(a -> a.contains("b"), 219 b -> b.length() > 11 220 ); 221 } 222 223 private void demo13(Predicate<String> p, Predicate<String> b) { 224 boolean helloWorld = p.negate().test("HelloWorld"); 225 System.out.println(helloWorld); 226 } 227 228 /** 229 * 数组当中有多条“姓名+性别”的信息如下,请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 230 * ArrayList 中,需要同时满足两个条件: 231 * 1. 必须为女生; 232 * 2. 姓名为4个字。 233 */ 234 @Test 235 public void test14() { 236 String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "杨颖,女", "杨幂,女"}; 237 List list = demo14(array, 238 s -> "女".equals(s.split(",")[1]), 239 s -> s.split(",")[0].length() == 4 240 ); 241 System.out.println(list); 242 243 } 244 245 private List demo14(String[] array, Predicate<String> p, Predicate<String> b) { 246 List data = new ArrayList<String>(); 247 for (String s : array) { 248 if (p.and(b).test(s)) 249 data.add(s); 250 } 251 return data; 252 } 253 254 255 /** 256 * java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 257 * 后者称为后置条件 258 */ 259 @Test 260 public void test15() { 261 String b = "5"; 262 demo15(b, s -> Integer.parseInt(s)); 263 264 265 } 266 267 private void demo15(String b, Function<String, Integer> f) { 268 Integer apply = f.apply(b); 269 System.out.println(apply + 10); 270 271 } 272 273 @Test 274 public void test16() { 275 String b = "5"; 276 demo16(b, 277 s -> Integer.parseInt(s), 278 c -> c *= 10); 279 280 281 } 282 283 private void demo16(String b, Function<String, Integer> f, Function<Integer, Integer> c) { 284 Integer apply = f.andThen(c).apply(b); 285 System.out.println(apply); 286 287 } 288 289 /** 290 * 请使用 Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为 291 * 1. 将字符串截取数字年龄部分,得到字符串; 292 * 2. 将上一步的字符串转换成为int类型的数字; 293 * 3. 将上一步的int数字累加100,得到结果int数字。 294 */ 295 @Test 296 public void test17() { 297 String nameAge="赵丽颖,18"; 298 299 int age = demo17(nameAge, s -> s.split(",")[1], 300 b -> Integer.parseInt(b), 301 n -> n += 100 302 ); 303 System.out.println(age); 304 305 } 306 private int demo17(String b, Function<String, String> one, Function<String, Integer> two,Function<Integer, Integer> three) { 307 return one.andThen(two).andThen(three).apply(b); 308 } 309 310 311 312 313 }