目录
一、函数式接口
概念
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是 Lambda,所以函数式接口就是可以适用于Lambda使用的接口。
public interface MyFunctionalInterface {
//定义一个抽象方法
public abstract void method();
//抽象方法中的public abstract可以省略,可以这样定义
void method2();
}
@FunctionalInterface注解
@FunctionalInterface的作用:检测一个接口是否是函数式接口。

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。

自定义函数式接口
对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:
- 实现类
public class MyFunctionalInterfaceImpl implements MyFunctionalInterface{
@Override //检测方法是否为重写的方法
public void method() {
System.out.println("重写了接口中的抽象方法");
}
}
- 测试类
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表达式是延迟执行的,这正好可以 作为解决方案,提升性能。
性能浪费的日志案例
日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。
public class Demo01Logger {
//定义一个根据日志的等级,显示日志信息的方法
public static void showlog(int level, String message) {
//对日志等级判断,等于1时输出日志信息
if(level == 1) {
System.out.println(message);
}
}
public static void main(String[] args) {
//定义三个日志信息
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showlog(1,msg1+msg2+msg3);
}
}
代码存在的问题:无论 level 是否满足要求,作为 showlog 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。
Lambda优化日志案例
public class Demo02Lambda {
public static void showlog(int level, MessageBuilder mb) {
if(level == 1) {
System.out.println(mb.builderMessage());
}
}
public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
showlog(1,()->{
System.out.println("Lambda执行!");
return msg1+msg2+msg3;
});
}
}
使用 Lambda 表达式作为参数传递时,仅仅是把参数传递到showlog方法中,只有满足日志等级是1级的条件,才会调用接口MessageBuilder中的方法builderMessage,再进行字符串拼接,如果不满足日志等级为1,接口MessageBuilder中的方法就不会执行,拼接字符串的代码也不会执行,所以就不会存在性能上的浪费。


实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。
使用Lambda作为参数和返回值
例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。
public class Demo03Runnable {
public static void startThread(Runnable r) {
new Thread(r).start();
}
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+"线程启动了。");
}
});
startThread(()-> System.out.println(Thread.currentThread().getName()+"-->"+"线程启动了。"));
}
}

如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一 个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。
import java.util.Arrays;
import java.util.Comparator;
public class Demo04Comparator {
public static Comparator<String> getComparator() {
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o2.length()-o1.length();
// }
// };
// return (String o1, String o2)->{
// return o2.length()-o1.length();
// };
return (o1,o2)->o2.length()-o1.length();
}
public static void main(String[] args) {
String[] arr = {"aaa","b","cccccc","ddddddddd"};
System.out.println("排序前:"+Arrays.toString(arr));
Arrays.sort(arr,getComparator());
System.out.println("排序后:"+Arrays.toString(arr));
}
}
三、函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。
Supplier接口
Supplier 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的 get 方法就会产生什么类型的数据。
import java.util.function.Supplier;
public class DemoSupplier {
public static String getString(Supplier<String> sup) {
return sup.get();
}
public static void main(String[] args) {
String s = getString(()->{
return "君君";
});
System.out.println(s);
String s1 = getString(()->"君君");
System.out.println(s1);
}
}
Customer接口
java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。
抽象方法:accept
Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。
import java.util.function.Consumer;
public class Demo01Consumer {
public static void method(String name, Consumer<String> con) {
con.accept(name);
}
public static void main(String[] args) {
method("君君在干嘛",(name)->{
//对传递的字符串进行消费
//消费方式:直接输出字符串
System.out.println(name);
//消费方式:反转字符串
String reName = new StringBuilder(name).reverse().toString();
System.out.println(reName);
});
}
}
默认方法:andThen
需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费。
注意:谁写前面谁先消费。
import java.util.function.Consumer;
public class Demo02AndThen {
public static void method(String s, Consumer<String> con1,Consumer<String> con2) {
// con1.accept(s);
// con2.accept(s);
//先执行con1 再执行 con2
con1.andThen(con2).accept(s);
}
public static void main(String[] args) {
method("Hello",(t)->{
//消费方式:把字符串转换为大写输出
System.out.println(t.toUpperCase()); //HELLO
}, (t)->{
//消费方式,把字符串转换为小写输出
System.out.println(t.toLowerCase()); //hello
});
}
}
Predicate接口
需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。
抽象方法:test
import java.util.function.Predicate;
public class Demo01Predicate {
public static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
}
public static void main(String[] args) {
String s = "ABCDE";
boolean b = checkString(s, (String str) -> {
//对参数传递的字符串进行自定义判断
return s.length() > 5;
});
System.out.println(b); //false
//优化Lambda
boolean b1 = checkString(s,str->s.length() > 4);
System.out.println(b1); //true
}
}
默认方法:and
表示并且关系,也可以用于连接两个判断条件,其JDK源码为:
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 Demo02Predicate_and {
public static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2) {
// return pre1.test(s) && pre2.test(s);
return pre1.and(pre2).test(s);
}
public static void main(String[] args) {
String s = "abfsgsgs";
boolean b = checkString(s,(String str)->{
return str.length()>6;
},(String str)->{
return str.contains("a");
});
System.out.println(b); //true
}
}
默认方法:or
与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:
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 Demo02Predicate_or {
public static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2) {
// return pre1.test(s) || pre2.test(s);
return pre1.or(pre2).test(s);
}
public static void main(String[] args) {
String s = "abfsg";
boolean b = checkString(s,(String str)->{
return str.length()>10;
},(String str)->{
return str.contains("a");
});
System.out.println(b); // true
}
}
默认方法:negate
JDK源代码为:
default Predicate<T> negate() {
return (t) ‐> !test(t);
}
测试类
import java.util.function.Predicate;
public class Demo02Predicate_negate {
public static boolean checkString(String s, Predicate<String> pre) {
return pre.negate().test(s);
}
public static void main(String[] args) {
String s = "abfsg";
boolean b = checkString(s,(String str)->{
return str.length()>10;
});
System.out.println(b); // true
}
}
Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。
抽象方法:apply
根据类型T的参数获取类型R的结果。
import java.util.function.Function;
public class Demo01Function {
public static void change(String s, Function<String,Integer> fun) {
Integer in = fun.apply(s);
System.out.println(in);
}
public static void main(String[] args) {
String s = "1234";
change(s,(String str)->{
return Integer.parseInt(str); //1234
});
change(s,str->Integer.parseInt(str)); //1234
}
}
默认方法:andThen
用来进行组合操作。JDK源代码如:
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 Demo02AndThen {
public static void change(String s,Function<String,Integer> fun1,Function<Integer,String> fun2) {
String ss = fun1.andThen(fun2).apply(s);
System.out.println(ss);
}
public static void main(String[] args) {
String s = "123";
change(s,(String str)->{
return Integer.parseInt(s)+10;
},(Integer i)->{
return i+""; //133
});
//优化
change(s,str->Integer.parseInt(s)+10,i->i+""); //133
}
}

2192

被折叠的 条评论
为什么被折叠?



