函数式接口(Functional Interface)是Java 8引入的一个新特性,它只有一个抽象方法的接口。这意味着你可以将一个函数式接口作为参数传递给方法,或者将其实现为一个lambda表达式。函数式接口的主要目的是允许你以声明性方式处理操作,比如函数的传递和返回。
函数式接口的特点包括:
1. **只有一个抽象方法**:这是函数式接口的核心特征,它确保了接口可以被用作lambda表达式的类型。
2. **可以有多个默认方法或静态方法**:除了一个抽象方法外,函数式接口还可以包含默认方法和静态方法。
3. **可以被隐式转换为lambda表达式**:由于只有一个抽象方法,Java编译器可以自动推断出lambda表达式中的参数和返回值。
4. **常用于实现函数式编程**:函数式接口是实现函数式编程的基础,它允许你以函数的方式处理数据和逻辑。
Java中一些内置的函数式接口包括:
- `java.util.function.Function<T,R>`:接受一个参数并返回一个结果。
- `java.util.function.Predicate<T>`:接受一个参数并返回一个布尔值。
- `java.util.function.Consumer<T>`:接受一个参数并执行某些操作,但不返回结果。
- `java.util.function.Supplier<T>`:不接受参数,返回一个结果。
- `java.util.function.BiFunction<T,U,R>`:接受两个参数并返回一个结果。
使用函数式接口可以简化代码,提高代码的可读性和可维护性。例如,你可以使用`java.util.stream.Stream`API来处理集合,其中就大量使用了函数式接口。
Java 8中引入了许多函数式接口,它们都位于`java.util.function`包中。以下是一些常用的函数式接口:
1. **`Supplier<T>`**:给定无参数,返回一个结果。
```java
T get()
```
2. **`Consumer<T>`**:接受一个参数,执行某些操作,但不返回结果。
```java
void accept(T t)
```
3. **`Predicate<T>`**:接受一个参数,并返回一个布尔值。
```java
boolean test(T t)
```
4. **`Function<T,R>`**:接受一个参数,并返回一个结果。
```java
R apply(T t)
```
5. **`BiFunction<T,U,R>`**:接受两个参数,并返回一个结果。
```java
R apply(T t, U u)
```
6. **`UnaryOperator<T>`**:是`Function<T,T>`的子接口,接受一个参数,并返回相同类型的结果。
```java
T apply(T t)
```
7. **`BinaryOperator<T>`**:是`BiFunction<T,T,T>`的子接口,接受两个相同类型的参数,并返回相同类型的结果。
```java
T apply(T t1, T t2)
```
8. **`Comparator<T>`**:用于比较两个参数,并返回一个整数值。
```java
int compare(T o1, T o2)
```
9. **`BiPredicate<T,U>`**:接受两个参数,并返回一个布尔值。
```java
boolean test(T t, U u)
```
10. **`BiConsumer<T,U>`**:接受两个参数,并执行某些操作,但不返回结果。
```java
void accept(T t, U u)
```
11. **`ToDoubleFunction<T>`**:接受一个参数,并返回一个`double`类型的结果。
```java
double applyAsDouble(T value)
```
12. **`ToIntFunction<T>`**:接受一个参数,并返回一个`int`类型的结果。
```java
int applyAsInt(T value)
```
13. **`ToLongFunction<T>`**:接受一个参数,并返回一个`long`类型的结果。
```java
long applyAsLong(T value)
```
14. **`DoubleUnaryOperator`**:接受一个`double`类型的参数,并返回一个`double`类型的结果。
```java
double applyAsDouble(double value)
```
15. **`IntUnaryOperator`**:接受一个`int`类型的参数,并返回一个`int`类型的结果。
```java
int applyAsInt(int value)
```
16. **`LongUnaryOperator`**:接受一个`long`类型的参数,并返回一个`long`类型的结果。
```java
long applyAsLong(long value)
```
17. **`DoubleBinaryOperator`**:接受两个`double`类型的参数,并返回一个`double`类型的结果。
```java
double applyAsDouble(double left, double right)
```
18. **`IntBinaryOperator`**:接受两个`int`类型的参数,并返回一个`int`类型的结果。
```java
int applyAsInt(int left, int right)
```
19. **`LongBinaryOperator`**:接受两个`long`类型的参数,并返回一个`long`类型的结果。
```java
long applyAsLong(long left, long right)
```
这些函数式接口为Java 8中的Lambda表达式和方法引用提供了基础,使得代码更加简洁和表达式化。
在Java 8中,`Stream`流是一种高级迭代器,它允许你以声明性方式处理数据集合。`Stream`提供了一种对集合对象进行一系列操作的新方式,这些操作可以是中间操作(如`filter`、`map`、`sorted`等)和终止操作(如`forEach`、`reduce`、`collect`等)。`Stream`流可以对数据序列执行各种操作,如筛选、转换、聚合等,而无需显式编写复杂的循环代码。
`Stream`流的主要特点包括:
1. **不可变性**:一旦创建了流,就不能再修改原始的元素集合。
2. **惰性求值**:流上的操作不会立即执行,而是在需要结果时才执行。
3. **可消费性**:流只能被消费一次。一旦完成,流就会失效,除非重新生成。
4. **并行能力**:流可以很容易地并行处理数据,以提高性能。
5. **类型安全**:流操作都是类型安全的,这意味着它们在编译时就会检查类型错误。
6. **函数式编程**:流与函数式接口紧密集成,允许你使用Lambda表达式作为参数。
`Stream`流的常见操作包括:
- **创建流**:可以通过多种方式创建流,如通过集合的`stream()`方法,数组的`Arrays.stream()`方法,或者通过`Stream`类的静态方法(如`of`、`iterate`、`generate`)。
- **中间操作**:这些操作会返回一个新的流,可以进行链式操作。例如:
- `filter`:根据条件过滤元素。
- `map`:将流中的每个元素映射到另一个元素。
- `flatMap`:将流中的每个元素替换为目标元素的流,然后将多个流连接成一个流。
- `sorted`:将流中的元素进行排序。
- **终止操作**:这些操作会消耗流,并产生一个最终的结果或副作用。例如:
- `forEach`:对流中的每个元素执行操作。
- `reduce`:通过某个连接动作将所有元素汇总成一个汇总结果。
- `collect`:将流转换成其他形式(如集合)。
- **短路操作**:某些终止操作在找到结果后会立即停止处理剩余的元素。例如`anyMatch`、`allMatch`、`noneMatch`。
使用`Stream`流可以提高代码的可读性和性能,特别是在处理大型数据集时。它允许开发者以声明性方式表达复杂的操作,而无需编写繁琐的循环和条件语句。
重复注解机制是Java 8引入的一个新特性,它允许在同一声明类型(类、属性或方法)上多次使用同一个注解。在Java 8之前,相同的注解在同一位置只能使用一次,不能使用多次。Java 8通过引入`@Repeatable`注解支持了重复注解机制,使得相同的注解可以在同一地方使用多次。
具体来说,要实现重复注解,需要定义一个注解,并使用`@Repeatable`元注解来标记它,`@Repeatable`的参数是一个容器注解,该容器注解用于存储重复的注解实例。例如,如果有一个注解`@Hint`,想要让它成为可重复的,可以这样定义:
```java
@Repeatable(Hints.class)
@interface Hint {
String value();
}
@interface Hints {
Hint[] value();
}
```
在Java 8之前,如果要应用多个相同的注解,需要使用一个容器注解来包含这些注解,然后将容器注解应用到元素上。而在Java 8中,可以直接在元素上多次使用相同的注解,如:
```java
@Hint("hint1")
@Hint("hint2")
class Person {}
```
这样,`Person`类就同时被`@Hint`注解标记了两次,分别带有值"hint1"和"hint2"。这种机制简化了代码,提高了可读性,并且在框架设计中特别有用,因为它允许为相同的元素标记多个不同的注解,实现更灵活的注解使用方式。
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
函数式接口可以对现有的函数友好地支持 lambda。
JDK 1.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