什么是函数式接口?

你好,我是猿java。

Lambda官方说明

上一篇文章我们分析了lambda是什么,里面提到一个点:lambda必须依赖于函数式接口,这篇博文我们就来一起分析下什么是函数式接口

首选我们看一个List遍历的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class ListTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);

//for method
for(int i = 0; i < list.size(); i++){
System.out.println("i=" + i);
}
// lambda method
list.forEach(System.out::println);
}

// forEach method
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}

/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);

/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

从上面的代码可以看出,从jdk8开始,list增加了forEach()方法进行遍历,forEach()接收一个Consumer类型的参数,进入Consumer可以看出该类有一个@FunctionalInterface
的注解,这个标识Consumer是一个函数式接口,接下来我们进入FunctionalInterface这个注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* An informative annotation type used to indicate that an interface
* type declaration is intended to be a <i>functional interface</i> as
* defined by the Java Language Specification.
*
* Conceptually, a functional interface has exactly one abstract
* method. Since {@linkplain java.lang.reflect.Method#isDefault()
* default methods} have an implementation, they are not abstract. If
* an interface declares an abstract method overriding one of the
* public methods of {@code java.lang.Object}, that also does
* <em>not</em> count toward the interface's abstract method count
* since any implementation of the interface will have an
* implementation from {@code java.lang.Object} or elsewhere.
*
* <p>Note that instances of functional interfaces can be created with
* lambda expressions, method references, or constructor references.
*
* <p>If a type is annotated with this annotation type, compilers are
* required to generate an error message unless:
*
* <ul>
* <li> The type is an interface type and not an annotation type, enum, or class.
* <li> The annotated type satisfies the requirements of a functional interface.
* </ul>
*
* <p>However, the compiler will treat any interface meeting the
* definition of a functional interface as a functional interface
* regardless of whether or not a {@code FunctionalInterface}
* annotation is present on the interface declaration.
*
* @jls 4.3.2. The Class Object
* @jls 9.8 Functional Interfaces
* @jls 9.4.3 Interface Method Body
* @jls 9.6.4.9 @FunctionalInterface
* @since 1.8
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

从源码中我们可以抽取FunctionalInterface注解的几个重要信息:

  • FunctionalInterface注解是从jdk1.8 开始引入
  • FunctionalInterface注解是一种信息性注释类型,用于指示接口类型声明旨在成为 Java 语言规范定义的功能接口
  • 函数式接口有且仅有一个抽象方法,因为所有的类默认继承java.lang.Object类,所以从Object类继承下来的抽象方法不会计入该接口的抽象方法总数
  • 函数式接口的实例有3种创建方式:lambda表达式、方法引用或构造函数引用
  • FunctionalInterface注解只能用于接口类型,而不能用于注释类型、枚举或类,如果此注解用于非接口,则编译器会生成错误信息
  • 只要接口符合函数式接口的特征,不论接口是否加了FunctionalInterface注解,编译器都会把这个接口当做函数式接口处理

学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

drawing