Java Lambda表达式
Java Lambda表达式概念函数式接口是指:有且仅有一个抽象方法的接口。 函数式接口即适用于函数式编程场景的接口。而Java中函数式编程体现的就是Lambda,所以Lambda接口就是适用于Lambda使用的接口。有且只有一个抽象方法,Java中的Lambda才能顺利推导。 格式只要确保接口的有且仅有一个抽象方法即可: 修饰符 interface 接口{ public abstract 返回值类型 方法名称(可选参数信息); //其他非抽象方法 } 接口中的public abstract可以省略,所以格式可以简化: 修饰符 interface 接口{ 返回值类型 方法名称(可选参数信息); //其他非抽象方法 } 函数式接口可以作为方法的参数和返回值类型 @FunctionalInterface注解@FunctionalInterface 修饰符 interface 接口{ 返回值类型 方法名称(可选参数信息); //其他非抽象方法 } 检测该函数是否是函数式接口,是否有且仅有一个抽象方法。 自定义函数式接口public class LambdaTest{ public static void useLambdaInterfaceMethod(LambdaInterface lifm){ lifm.LambdaInterfaceMethod(); } public static void main(String[] args){ //传入接口的实现类: //useLambdaInterfaceMethod(new 接口的实现类); //传入接口的匿名内部类 /* useLambdaInterfaceMethod(new LambdaInterface(){ public void LambdaInterfaceMethod(){ System.out.println("这里传入的是接口的实现匿名内部类"); } }); */ //抽象方法有且仅有一行语句,可以省略大括号 //ustLambdaInterfaceMethod(() -> System.out.println("...")); useLambdaInterfaceMethod(() -> { System.out.println("使用Lambda表达式,重写接口中的抽象方法。") } ); } } //注解FunctionanInterface检测是否为函数式接口 @FunctionalInterface interface LambdaInterface{ void LambdaInterfaceMethod(); } 函数式编程Lambda的延迟执行有些场景代码执行以后,结果不一定使用,从而造成浪费。而Lambda表达式是延迟执行的,可以作为解决方案,提升性能。 性能浪费的日志案例日志可以快速定位问题,记录程序运行中的情况,以便项目的监控和优化。 一种场景是对参数进行有条件的使用,例如在日子消息拼接以后,在满足条件时打印输出。 /* 下面存在性能浪费的问题 调用log方法的时候,传入第二个参数是一个拼接后的字符串 是先把字符串拼接好,然后再调用log方法 log方法中的日志等级不是1级,那么就不会是如此拼接后的字符串 这里的字符串就白拼接了,造成浪费 */ public class LoggerTest{ public static void log(int level,String msg){ if(level == 1){ System.out.println(msg); } } public static void main(String[] args){ String str1 = "hello"; String str2 = "java"; log(1,str1 + str2); } } 体验Lambda的更优写法/* 使用Lambda优化日志: 延迟加载 使用前提:必须存在函数式接口 使用Lambda表达式作为参数传递,仅仅是把参数传递到log方法中, 只有满足条件,日志的等级是1级 才会调用接口LambdaInterface方法中的msgAdd()方法 然后进行方法的拼接 如果条件不满足,那么接口LambdaInterface方法中的msgAdd()方法不会执行 拼接字符串的代码也不会执行,不会存在性能的浪费 */ public class LoggerTest{ public static void log(int level,LambdaInterface li){ if(level == 1){ String str = li.msgAdd(); System.out.println(str); } } public static void main(String[] args){ String str1 = "hello"; String str2 = "java"; log(1,() -> { //返回一个拼接好的字符串 return str1 + str2; }); } } @FunctionalInterface interface LambdaInterface{ //定义一个拼接消息的方法,返回拼接的消息 String msgAdd(); } 证明Lambda的延迟public static void main(String[] args){ String str1 = "hello"; String str2 = "java"; log(1,() -> { //没有满足条件这里不会执行 System.out.println("不满足条件"); //返回一个拼接好的字符串 return str1 + str2; }); } 使用Lambda作为参数和返回值Java的Lambda表达式可以被当做匿名内部类的替代品,如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。 public class Demo{ private static void startThread(Runnable runab){ new Thread(runab).start(); } public static void main(String[] args){ startThread(() -> System.out.println(...)); } } public class DemoComparator{ //返回值类型是一个函数式接口 //Comparator只有一个抽象方法,所以是函数式接口 private static Comparator<String> newComparator(){ return (a,b) -> b.length() - a.length(); } public static void main(String[] args){ String[] arr = {"a","abs","dfae"}; //排序 Arrays.sort(arr,newComparator()); } } 如果返回值类型是一个函数式接口,就可以直接返回一个Lambda表达式,如上程序。 注:Comparator有两个抽象方法,int cimpare(T o1,T o2)和boolean equals(Object obj),如果接口声明了一个覆盖java.lang.Object的全局方法之一的抽象方法,那么它不会计入接口的抽象方法数量中,因为接口的任何实现都将具有java.lang.Object或其他地方的实现。 常用的函数式接口Supplier接口java.util.function.Supplier
import java.util.function.Supplier; public class SupplierClass{ public static String getString(Supplier<String> sup){ return sup.get(); } public static void main(String[] args){ String str1 = "hello"; String str2 = "java"; String getStringMethod = getString(() -> str1 + str2); System.out.println(getStringMethod); } } Supplier
求数组最大值练习import java.util.function.Supplier; public class SupplierClass{ //返回的是Integer类型 public static Integer getMax(Supplier<Integer> sup){ return sup.get(); } public static void main(String[] args){ int[] iArr = {12,252,-12,435}; int getMaxInteger = getMax(() -> { int max = iArr[0]; //遍历数组 for(int i : iArr){ //如果数组有一个值大于max,则把这个值赋值给max if(i > max){ max = i; } } //返回数组中的最大值 return max; }); System.out.println(getMaxInteger); } } Consumer接口java.util.function.Consumer
import java.util.function.Consumer; public class ConsumerClass{ public static void consumerUseMethod(Consumer<String> com){ con.accept("被使用的字符串"); } public static void main(String[] args){ consumerUseMethod((x) -> System.out.println(x)); } } 抽象方法:Acceptimport java.util.function.Consumer; public class ConsumerClass{ public static void consumerUseMethod(String name,Consumer<String> con){ con.accept(name); } public static void main(String[] args){ consumerUseMethod("lilei",(x) -> System.out.println(x)); } } 抽象默认方法:andThen如果一个方法的参数和返回值全是Consumer类型,就可以实现这个效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中default方法antThen: default Consumer<T> andThen(Consumer<? super T> after){ Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } java.util.Objects和requireNonNull静态方法将会在参数为null时主动抛出异常。省去重写if语句和抛出空指针异常的麻烦。 import java.util.function.Consumer; /* 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); */ public class ConsumerClass{ public static void consumerUseMethod(String name,Consumer<String> con,Consumer<String> con2){ //con.accept(name); //con2.accept(name); //使用andThen方法替代上面代码: con.andThen(con2).accept(name); //con链接的con2,所以先执行con,再执行con2 } public static void main(String[] args){ consumerUseMethod( "lilei",//第一个Lambda表达式 (x) -> System.out.println(x),//第二个Lambda表达式 (x) -> System.out.println("第二个Lambda表达式") ); } } 如果需要多个Consumer接口,使用andThen方法,就是con1.andThen(con2).andThen(con3)...andThen(conN).accept(t); Predicate接口包含一个抽象方法:boolean test(T t),用于条件判断场景 import java.util.function.Predicate; public class PredicateClass{ public static boolean checkString(String s,Predicate<String> pre){ //传入要判断的文本 return pre.test(s); } public static void main(String[] args){ String str = "guess how long"; //判断字符串长度是否大于5,并返回结果 boolean b = checkString(str,(String s) -> {return str.length() > 5;}); System.out.println(b); } } 省略一些代码 //省略String s 中类型,可以直接推导 //只有一行语句,省略大括号和return关键字 boolean b = checkString(str,s-> str.length() > 5); 默认方法and(与)将两个Predicate条件使用与逻辑连接起来实现并且的效果,使用默认方法and default Predicate<T> and(Predicate<? super T> other){ Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } 实现: import java.util.function.Predicate; public class PredicateClass{ public static void checkString(String str,Predicate<String> preOne,Predicate<String> preTwo){ //preOne.test("str")&&preTwo.test("str"); boolean b = preOne.and(preTwo).test(str); System.out.println(b); } public static void main(String[] args){ String strName = "helloworld"; checkString(strName,s-> s.contains("H"),s-> s.contains("W")); } } 默认方法or(或)default Predicate<T> or(Predicate<? super T> other){ Objects.requireNonNull(Other); return (t) -> test(t) || other.test(t); } 实现: import java.util.function.Predicate; public class PredicateClass{ public static boolean checkString(String str,Predicate<String> preTwo){ //等价于preOne.test(t) || preTwo.test(t); return preOne.or(preTwo).test(str); } public static void main(String[] args){ String strName = "helloworld"; boolean b = checkString(strName,s-> s.contains("h"),s-> s.contains("W")); System.out.println(b); } } 默认方法negate(非)default Predicate<T> negate(){ retuan (t) -> !test(t); } //取反 return preOne.negate(preTwo).test(str); Function接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。 抽象方法applyFunction最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。 import java.util.function.Function; public class FunctionClass{ //Function<原始类型, 要转换的类型> public static void applyMethod(String str,Function<String,Integer> fun){ //字符串类型转换成int类型 int num = fun.apply(str); System.out.println(num + 20); } public static void main(String[] args){ String str = "100"; applyMethod(str,s -> Integer.parseInt(s)); } } 默认方法andThen//用来组合操作 default <V> Function<T,V> andThen(Function<? super R,? extends V> after){ Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } 实现代码: import java.util.function.Function; public class FunctionClass{ //andThen组合操作 public static void applyMethod(String str,Integer> funOne,Function<Integer,String> funTwo){ //字符串类型转换成int类型,加10 //int num = fun.apply(str) + 10; //然后转换成String类型 //String str = funTwo.apply(num); //组合,先实现funOne,再实现funTwo String strr = funOne.andThen(funTwo).apply(str); System.out.println(strr); } public static void main(String[] args){ String str = "100"; //先把字符串转换为integer,结果加10 //把前面funOne的结果转换成String类型 applyMethod(str,(String s) -> Integer.parseInt(s) + 10,(Integer in) -> String.valueOf(in)); } } Lambda方法引用方法引用符号双冒号::是为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达是函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。 语义分析 例如:System.out对象中有一个重载的println(String)方法恰好就是我们所需要的,那么对于该方法的函数式接口参数,以下两种方式等价: Lambda表达式: s -> System.out.println(s); 方法引用写法: System.out::println 第一种语义是:拿到参数之后经Lambda手,继而传递给System.out.println方法去处理; 两种写法的执行效果完全一样。而第二种方法引用复用了已有方案,更加简洁。 Lambda中传递的参数一定是方法引用中那个方法可以接收的类型,否则抛出异常。 推导与省略 如果使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式--它们都是自动推导,而如果使用方法引用,也是同样可以根据上下文进行推导。 通过对象名引用成员方法/* 通过对象名引用成员方法 使用前提:对象名已经存在,成员方法已经存在 */ public class LambdaObjectUseMethod{ //定义一个方法 public static void printString(Printable p){ p.print("hello"); } public static void main(String[] args){ //Lambda表达式 printString(x -> { MethodUpper mu = new MethodUpper(); mu.printUpperCaseString(x); }); //方法引用 //对象必须已经存在 MethodUpper mu2 = new MethodUpper(); printString(mu2::printUpperCaseString); } } //函数式接口 @FunctionalInterface interface Printable{ void print(String s); } //将字符串全改为大写并输出 class MethodUpper{ public void printUpperCaseString(String str){ System.out.println(str.toUpperCase()); } } 通过类名称引用静态方法/* 通过对象名引用成员方法 使用前提:类已经存在,静态方法已经存在 */ public class LambdaObjectUseMethod{ //定义一个方法 public static void printString(Printable p){ p.print("hello"); } public static void main(String[] args){ //通过类名引用静态方法 printString(MethodUpper::printUpperCaseString); } } //函数式接口 @FunctionalInterface interface Printable{ void print(String s); } //将字符串全改为大写并输出 class MethodUpper{ //静态方法 public static void printUpperCaseString(String str){ System.out.println(str.toUpperCase()); } } 通过super引用成员方法//函数式接口 @FunctionalInterface interface Greetable{ void greet(); } //父类 class FatherClass{ public void showMethod(){ System.out.println("我是你爸爸"); } } public class SonClass extends FatherClass{ public void showMethod(){ System.out.println("不符solo!!!"); } public void useGreetable(Greetable gt){ gt.greet(); } public void show(){ /* useGreetable(() -> { FatherClass fc = new SonClass(); super.showMethod(); }); */ //super引用成员方法 useGreetable(super::showMethod); } public static void main(String[] args){ new SonClass().show(); } } 通过this引用成员方法//函数式接口 @FunctionalInterface interface Greetable{ void greet(); } //父类 class FatherClass{ public void showMethod(){ System.out.println("我是你爸爸"); } } public class SonClass extends FatherClass{ public void showMethod(){ System.out.println("不符solo!!!"); } public void useGreetable(Greetable gt){ gt.greet(); } public void show(){ //this引用本类成员方法 useGreetable(this::showMethod); } public static void main(String[] args){ new SonClass().show(); } } 类构造器的引用构造器引用使用 类名称::new 的格式 //类构造器引用 class Person{ String name; public Person(String name){ this.name = name; } public String getName(){ return name; } public void setName(String name){ this.name = name; } } @FunctionalInterface interface CreatePerson{ Person returnPerson(String name); } public class PersonFunctionClass{ public static void method(String name,CreatePerson cp){ Person p = cp.returnPerson(name); //Person p = System.out.println(p.getName()); } public static void main(String[] args){ /*Lambda表达式 method("lilei",(String name) -> { return new Person(name); }); */ /*简化版的Lambda表达式 method("lilei",name -> new Person(name)); */ //构造器引用 method("lilei",Person::new); } } 数组构造器的引用//数组的构造器引用 @FunctionalInterface interface ArrayBuiler{ //创建int数组类型的方法 int[] createIntArray(int length); } //数组的构造器引用 public class ArrayTestClass{ //定义一个方法,方法参数传递创建数组的长度和ArrayBuiler接口 //方法内部根据传递的长度创建数组 public static int[] createArray(int length,ArrayBuiler ab){ return ab.createIntArray(length); } public static void main(String[] args){ int[] arr1 = createArray(10,(int i) -> { return new int[i]; }); int[] arr2 = createArray(12,len -> new int[len]); //引用数组的构造器 int[] arr3 = createArray(13,int[]::new); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |