函数式接口是指:有且仅有一个抽象方法的接口。java
函数式接口即适用于函数式编程场景的接口。而Java中函数式编程体现的就是Lambda,因此Lambda接口就是适用于Lambda使用的接口。有且只有一个抽象方法,Java中的Lambda才能顺利推导。编程
只要确保接口的有且仅有一个抽象方法便可:数组
修饰符 interface 接口{ public abstract 返回值类型 方法名称(可选参数信息); //其余非抽象方法 }
接口中的public abstract能够省略,因此格式能够简化:app
修饰符 interface 接口{ 返回值类型 方法名称(可选参数信息); //其余非抽象方法 }
函数式接口能够做为方法的参数和返回值类型函数式编程
@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表达式是延迟执行的,能够做为解决方案,提高性能。性能
日志能够快速定位问题,记录程序运行中的状况,以便项目的监控和优化。优化
一种场景是对参数进行有条件的使用,例如在日子消息拼接之后,在知足条件时打印输出。ui
/* 下面存在性能浪费的问题 调用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表达式做为参数传递,仅仅是把参数传递到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(); }
public static void main(String[] args){ String str1 = "hello"; String str2 = "java"; log(1, () -> { //没有知足条件这里不会执行 System.out.println("不知足条件"); //返回一个拼接好的字符串 return str1 + str2; }); }
Java的Lambda表达式能够被当作匿名内部类的替代品,若是方法的参数是一个函数式接口类型,那么就可使用Lambda表达式进行替代。使用Lambda表达式做为方法参数,其实就是使用函数式接口做为方法参数。
例如:Runnable接口就是一个函数式接口this
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或其余地方的实现。
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); } }
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)); } }
import 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)); } }
若是一个方法的参数和返回值全是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);
包含一个抽象方法:boolean test(T t),用于条件判断场景
符合条件:返回true
不符合:返回false
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);
将两个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")); } }
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> preOne, 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); } }
default Predicate<T> negate(){ retuan (t) -> !test(t); }
//取反 return preOne.negate(preTwo).test(str);
用来根据一个类型的数据获得另外一个类型的数据,前者称为前置条件,后者称为后置条件。
Function最主要的抽象方法为: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)); } }
//用来组合操做 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, Function<String, 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的替代者。
语义分析
例如:System.out对象中有一个重载的println(String)方法刚好就是咱们所须要的,那么对于该方法的函数式接口参数,如下两种方式等价:
Lambda表达式: s -> System.out.println(s); 方法引用写法: System.out::println
第一种语义是:拿到参数以后经Lambda手,继而传递给System.out.println方法去处理;
第二种是:直接让System.out中的println方法来取代Lambda。
两种写法的执行效果彻底同样。而第二种方法引用复用了已有方案,更加简洁。
Lambda中传递的参数必定是方法引用中那个方法能够接收的类型,不然抛出异常。
推导与省略
若是使用Lambda,那么根据可推导就是可省略的原则,无需指定参数类型,也无需指定重载形式--它们都是自动推导,而若是使用方法引用,也是一样能够根据上下文进行推导。
函数式接口是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()); } }
//函数式接口 @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(); } }
//函数式接口 @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); } }