1. 函数式接口
只有一个抽象方法的接口, 被称为函数式接口, 可以使用@FunctionalInterface注解来表明(如果符合函数式接口规范, 即便不使用该注解, 编译器依然会将其认定为函数式接口) 。
这里需要注意的是, 函数式接口规范中, “只能有一个抽象方法”, 并不是说该接口中只能有一个方法。 Java8以后, 接口中存在实例方法; 同时, 任何被Object实现的方法, 均不会被视为抽象方法。 因此如下的定义是完全符合函数式接口规范的(来自JDK的Function接口):
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
2. 接口默认方法
如上面的示例, JDK1.8以后, Java使用default关键字, 在接口里定义默认行为,基于这一特性, 我们可以在Java中实现类C++的多继承机制, 同时也会引入多继承导致的行为二义性问题。
3. lambda表达式
lambda表达式即匿名函数, 是没有函数名的函数体, 可以直接作为参数传递给调用者, 是函数式编程的核心内容。下面是个lambda表达式的实际应用, 在一个枚举中, 通过stream的filter遍历查询匹配的枚举对象:
public enum RuleAutoFlag {
OPEN_TRUE(1, "开通"),
OPEN_FALSE(0, "不开通"),;
RuleAutoFlag(int code, String name) {
this.code = code;
this.name = name;
}
private Integer code;
private String name;
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
public static RuleAutoFlag getByCode(Integer code) {
return Arrays.stream(RuleAutoFlag.values()).filter(a -> a.getCode().equals(code)).findFirst().orElse(null);
}
}
需要注意的是, 传入lambda表达式中的变量, 即便显示声明为final, 编译器也会将其处理为final, 因此不允许其再表达式内部进行修改。
4 方法引用
方法引用是Java8中用来简化lambda表达式的一种写法, 其通过“类名::方法名”来定位到一个静态方法或实例方法, 具体使用不在此过多赘述。