【Java8】函数式接口
摘要
- 何为函数式接口?
- 什么是lambda表达式,lambda表达式的本质;
- 函数式接口与lambda表达式的联系:lambda是实现函数式接口的一个快捷方式,可以作为函数式接口的一个实例;
- 常用Java8内置的函数式接口 Function、Predicate、Consumer 和 Supplier 介绍;
函数式接口
何为函数式接口?
函数式接口也是 java interface 的一种,但还需要满足:
- 一个函数式接口只有一个抽象方法(SAM,single abstract method);
- 函数式接口可以有默认方法和静态方法;
- 函数式接口可以用
@FunctionalInterface
注解进行修饰。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
满足这些条件的 interface,就可以被视为函数式接口。例如,java8中的Comparator接口:
@FunctionalInterface
public interface Comparator<T> {
/**
* single abstract method
* @since 1.8
*/
int compare(T o1, T o2);
/**
* Object 类中的 public abstract method
* @since 1.8
*/
boolean equals(Object obj);
/**
* 默认方法
* @since 1.8
*/
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
......
/**
* 静态方法
* @since 1.8
*/
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
.....
}
函数式接口有什么用?
一句话,函数式接口带给我们最大的好处就是:可以使用极简的lambda表达式实例化接口。
原始匿名内部类方式:
class Test {
public static void main(String args[]) {
List<Person> persons = new ArrayList<Person>();
Collections.sort(persons, new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getAge(), o2.getAge());
}
});
}
}
lambda简化后:
Comparator<Person> comparator = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());
常用的函数式接口
函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 这类接口只定义了唯一的抽象方法的接口,并且这类接口使用了@FunctionalInterface
进行注解。在jdk8中,引入了一个新的包java.util.function
, 可以使java 8 的函数式编程变得更加简便。这个package中的接口大致分为了以下四类:
- Function: 接收参数,并返回结果,主要方法
R apply(T t)
- Consumer: 接收参数,无返回结果, 主要方法为
void accept(T t)
- Supplier: 不接收参数,但返回结构,主要方法为
T get()
- Predicate: 接收参数,返回boolean值,主要方法为
boolean test(T t)
java.util.function包中所有的接口整理:
Lambda 基础语法
语法形式为() -> {}
,其中 ()
用来描述参数列表,{}
用来描述方法体,->
为 lambda运算符 ,读作(goes to)。
Lambda 语法简化
我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。
Copyimport lambda.interfaces.*;
public class Test2 {
public static void main(String[] args) {
//1.简化参数类型,可以不写参数类型,但是必须所有参数都不写
NoReturnMultiParam lamdba1 = (a, b) -> {
System.out.println("简化参数类型");
};
lamdba1.method(1, 2);
//2.简化参数小括号,如果只有一个参数则可以省略参数小括号
NoReturnOneParam lambda2 = a -> {
System.out.println("简化参数小括号");
};
lambda2.method(1);
//3.简化方法体大括号,如果方法条只有一条语句,则可以胜率方法体大括号
NoReturnNoParam lambda3 = () -> System.out.println("简化方法体大括号");
lambda3.method();
//4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号
ReturnOneParam lambda4 = a -> a+3;
System.out.println(lambda4.method(5));
ReturnMultiParam lambda5 = (a, b) -> a+b;
System.out.println(lambda5.method(1, 1));
}
}
Function
Java8 引入了函数式接口,并在java.util.function 包内预定义了常用函数式接口,下表罗列了一些常用的函数式接口:
/**
* Represents a function that accepts one argument and produces a result.
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*/
R apply(T t);
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
一个实例如下:
public class FunctionTest {
public static void main(String[] args) {
Function<Integer, Integer> func1 = item -> item + 1;
Function<Integer, Integer> func2 = item -> item * 3;
System.out.println(func1.compose(func2).apply(2)); //7
System.out.println(func1.andThen(func2).apply(2)); //9
}
}
Predicate
Predicate接口接受一个输入参数,返回一个布尔值结果。其定义为:
/**
* Represents a predicate (boolean-valued function) of one argument.
* @param <T> the type of the input to the predicate
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*/
boolean test(T t);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
其默认方法也封装了and、or和negate逻辑,看个例子:
public static void main(String[] args) {
Predicate<Person> predicate1 = (Person person) -> {
Integer age = person.getAge();
return age >= 18;
};
Predicate<Person> predicate2 = (Person person) -> {
String name = person.getName();
return name.startsWith("R");
};
Person rico = new Person();
rico.setName("Rico");
rico.setAge(16);
System.out.println("Rico名字以R开头且年满18岁 : " + predicate1.and(predicate2).test(rico));
System.out.println("Rico名字以R开头或年满18岁 : " + predicate1.or(predicate2).test(rico));
System.out.println("Rico名字不是以R开头 : " + predicate1.negate().test(rico));
}
/** output
Rico名字以R开头且年满18岁 : false
Rico名字以R开头或年满18岁 : true
Rico名字不是以R开头 : true
*/
其他Predicate接口:
- BiPredicate:boolean test(T t, U u);接受两个参数的二元谓词
- DoublePredicate:boolean test(double value);入参为double的谓词函数
- IntPredicate:boolean test(int value);入参为int的谓词函数
- LongPredicate:boolean test(long value);入参为long的谓词函数
(126条消息) Java 8 Predicate花样用法_明明如月学长的博客-CSDN博客_predicate用法
Supplier
Supplier接口不需要任何参数,返回一个值。其定义为:
/**
* Represents a supplier of results.
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*/
T get();
}
看个例子:
public static void main(String[] args) {
Person rico = new Person();
rico.setName("Rico");
rico.setAge(16);
Supplier<Person> supplier = () -> rico;
System.out.println("supplier : " + supplier.get());
}
/** output
supplier : {"age":16,"name":"Rico"}
*/
其他Supplier接口:
- BooleanSupplier:boolean getAsBoolean(); 返回boolean
- DoubleSupplier:double getAsDouble();返回double
- IntSupplier:int getAsInt();返回int
- LongSupplier:long getAsLong();返回long
Consumer
Consumer接口接受一个输入参数,没有返回值。其定义为:
/**
* 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.
* * @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.
*/
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.
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
示例:
public class ConsumerTest {
public static void main(String[] args) {
Consumer<int[]> consumer = arr -> System.out.println(Arrays.toString(arr));
int[] arr = {1,2,3,5};
consumer.accept(arr); //[1, 2, 3, 5]
}
}
其他Consumer接口:
- BiConsumer:void accept(T t, U u);接受两个参数
- DoubleConsumer:void accept(double value);接受一个double参数
- IntConsumer:void accept(int value);接受一个int参数
- LongConsumer:void accept(long value);接受一个long参数
- ObjDoubleConsumer:void accept(T t, double value);接受一个泛型参数一个double参数
- ObjIntConsumer:void accept(T t, int value);接受一个泛型参数一个int参数
- ObjLongConsumer:void accept(T t, long value);接受一个泛型参数一个long参数
Lambda 表达式常用示例
Lambda 表达式中的闭包问题
这个问题我们在匿名内部类中也会存在,如果我们把注释放开会报错,告诉我 num 值是 final 不能被改变。这里我们虽然没有标识 num 类型为 final,但是在编译期间虚拟机会帮我们加上 final 修饰关键字。
Copyimport java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
int num = 10;
Consumer<String> consumer = ele -> {
System.out.println(num);
};
//num = num + 2;
consumer.accept("hello");
}
}