Java 8 把一大块新能力摊在一张图上:Lambda、流、新日期、Optional、CompletableFuture……本文只把函数式接口和 lambda 讲清楚——lambda 不能单独出现,必须装进一个 SAM 接口里。
先看全景(来自网络),再落到接口、方法引用、变量捕获和内置 Function 家族。

一、函数式接口
函数式接口本质仍是接口,但是 SAM(Single Abstract Method):只有一个需要实现的抽象方法。以它为参数的方法,调用时才能传入 lambda。反过来:能传入 lambda 的参数类型,一定是函数式接口,通常带 @FunctionalInterface。
-
静态方法和
default 方法不算进 SAM 契约,可以任意加(default 多为兼容老 API)。
-
覆盖 Object 的
equals / toString / hashCode 也不破坏函数式接口。
-
注解不是必须的,但写上能防止同事再往接口里塞第二个抽象方法。
JDK 8 之前已经存在、后来被标成函数式接口的例子:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
lambda 的方法体就是该抽象方法的实现:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
二、方法与构造函数引用
上面转换可写成静态方法引用:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
:: 也能引用实例方法:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"
构造函数引用要先有工厂接口,编译器按 create 的签名选合适的构造:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
如果是静态方法,则是ClassName::methodName。如 Object ::equals
如果是实例方法,则是Instance::methodName。如Object obj=new Object();obj::equals;
构造函数.则是ClassName::new
三、Lambda 的变量范围
对外部变量的访问粒度和匿名类很像:能用到外围的 effectively final 局部变量,以及成员/静态变量。
访问局部变量
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
和匿名类不同,不必显式写 final,但编译期仍当 final 用:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
后面再改 num,或在 lambda 里改它,都不合法:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3;
成员变量和静态变量
局部变量只读,成员和静态变量在 lambda 里可读可写:
class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
}
默认方法进不了 lambda
接口里的 default 方法能给匿名类实例用,但 lambda 内部不能直接调。下面这行编译不过:
Formula formula = (a) -> sqrt(a * 100);
四、内置函数式接口
JDK 8 内置了一组:Consumer、Supplier、Function、Predicate、BinaryOperator 等,老接口如 Comparator、Runnable 也可标 @FunctionalInterface 开 Lambda。

消费型:
public static void donation(Integer money, Consumer<Integer> consumer){
consumer.accept(money);
}
public static void main(String[] args) {
donation(1000, money -> System.out.println("好心的麦乐迪为Blade捐赠了"+money+"元")) ;
}
供给型:
public static List<Integer> supply(Integer num, Supplier<Integer> supplier){
List<Integer> resultList = new ArrayList<Integer>() ;
for(int x=0;x<num;x++)
resultList.add(supplier.get());
return resultList ;
}
public static void main(String[] args) {
List<Integer> list = supply(10,() -> (int)(Math.random()*100));
list.forEach(System.out::println);
}
函数型(字符串转 Integer):
public static Integer convert(String str, Function<String, Integer> function) {
return function.apply(str);
}
public static void main(String[] args) {
Integer value = convert("28", x -> Integer.parseInt(x));
}
断言型(筛两个字的水果):
public static List<String> filter(List<String> fruit, Predicate<String> predicate){
List<String> f = new ArrayList<>();
for (String s : fruit) {
if(predicate.test(s)){
f.add(s);
}
}
return f;
}
public static void main(String[] args) {
List<String> fruit = Arrays.asList("香蕉", "哈密瓜", "榴莲", "火龙果", "水蜜桃");
List<String> newFruit = filter(fruit, (f) -> f.length() == 2);
System.out.println(newFruit);
}
自己写的方法若要收 lambda,参数用这些接口即可:
// Function<T, R> -T作为输入,返回的R作为输出
Function<String,String> function = (x) -> {System.out.print(x+": ");return "Function";};
System.out.println(function.apply("hello world"));
//Predicate<T> -T作为输入,返回的boolean值作为输出
Predicate<String> pre = (x) ->{System.out.print(x);return false;};
System.out.println(": "+pre.test("hello World"));
//Consumer<T> - T作为输入,执行某种动作但没有返回值
Consumer<String> con = (x) -> {System.out.println(x);};
con.accept("hello world");
//Supplier<T> - 没有任何输入,返回T
Supplier<String> supp = () -> {return "Supplier";};
System.out.println(supp.get());
//BinaryOperator<T> -两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用
BinaryOperator<String> bina = (x,y) ->{System.out.print(x+" "+y);return "BinaryOperator";};
System.out.println(" "+bina.apply("hello ","world"));
五、什么是 lambda,语法长什么样
lambda 是一段可传递的代码:把「传数据」变成「传行为」。Java 8 之前写线程,匿名内部类要围六行样板:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("do something.");
}
}
lambda 只需:
Runnable r = () -> System.out.println("do something.");
语法三块:逗号分隔的形式参数(即 SAM 方法的参数)→ 箭头 -> → 方法体(表达式或 {} 代码块;若 SAM 返回 void 可以不加大括号)。

| 接口 |
输入 |
输出 |
Function<T,R> |
T |
R |
Predicate<T> |
T |
boolean |
Consumer<T> |
T |
无 |
Supplier<T> |
无 |
T |
BinaryOperator<T> |
两个 T |
一个 T(适合 reduce) |
参考:winterbe;译文 http://www.importnew.com/10360.html;跟上 Java 8;10 个示例;Optional;Optional 深度解析。
一句话总结:lambda 必须落在函数式接口上;方法引用用 :: 少写样板;捕获的局部变量要是 effectively final;日常优先用 JDK 自带的 Function / Predicate / Consumer / Supplier。