Java 8 函数式接口使用示例及相关新特性
简介
函数式接口(Functional Interface)是Java 8对一类特殊类型的接口的称呼。 函数式接口下共有
1、三种方法
-
唯一的抽象方法
-
使用default定义普通方法(默认方法),通过对象调用。 实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写 。当然如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。签名跟接口default方法一致,但是不能再加default修饰符。
-
使用static定义静态方法,通过接口名调用。
2、一个新注解
- 如果现在某一个接口就是为了函数式接口而生的,定义的时候就让其只有一个抽象方法,所以有了一个新的注解:函数式接口
@FunctionInterface
3、四种类型
在jdk8中,引入了一个新的包java.util.function
, 可以使java 8 的函数式编程变得更加简便。这个package中的接口大致分为了以下四类:
名称 | 描述 |
---|---|
Function<T,R> | 接受几个输入参数,返回一个结果 |
Consumer<T> | 代表接受一个输入参数并且无返回的操作 |
Predicate | 接收一个输入参数,返回一个布尔值结果 |
Supplier | 无参数,返回一个结果 |
- Function: 接收参数,并返回结果,主要方法
R apply(T t)
- Consumer: 接收参数,无返回结果, 主要方法为
void accept(T t)
- Supplier: 不接收参数,但返回结构,主要方法为
T get()
- Predicate: 接收参数,返回boolean值,主要方法为
boolean test(T t)
Function
表示一个方法接收参数并返回结果。
接收单个参数
Interface | functional method | 说明 |
---|---|---|
Function<T,R> | R apply(T t) | 接收参数类型为T,返回参数类型为R |
IntFunction | R apply(int value) | 以下三个接口,指定了接收参数类型,返回参数类型为泛型R |
LongFunction | R apply(long value) | |
Double | R apply(double value) | |
ToIntFunction | int applyAsInt(T value) | 以下三个接口,指定了返回参数类型,接收参数类型为泛型T |
ToLongFunction | long applyAsLong(T value) | |
ToDoubleFunction | double applyAsDouble(T value) | |
IntToLongFunction | long applyAsLong(int value) | 以下六个接口,既指定了接收参数类型,也指定了返回参数类型 |
IntToDoubleFunction | double applyAsLong(int value) | |
LongToIntFunction | int applyAsLong(long value) | |
LongToDoubleFunction | double applyAsLong(long value) | |
DoubleToIntFunction | int applyAsLong(double value) | |
DoubleToLongFunction | long applyAsLong(double value) | |
UnaryOperator | T apply(T t) | 特殊的Function,接收参数类型和返回参数类型一样 |
IntUnaryOperator | int applyAsInt(int left, int right) | 以下三个接口,制定了接收参数和返回参数类型,并且都一样 |
LongUnaryOperator | long applyAsInt(long left, long right) | |
DoubleUnaryOperator | double applyAsInt(double left, double right) |
接收两个参数
interface | functional method | 说明 |
---|---|---|
BiFunction<T,U,R> | R apply(T t, U u) | 接收两个参数的Function |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | 以下三个接口,指定了返回参数类型,接收参数类型分别为泛型T, U |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | |
ToDoubleBiFunction<T,U> | double appleyAsDouble(T t, U u) | |
BinaryOperator | T apply(T t, T u) | 特殊的BiFunction, 接收参数和返回参数类型一样 |
IntBinaryOperator | int applyAsInt(int left, int right) | |
LongBinaryOperator | long applyAsInt(long left, long right) | |
DoubleBinaryOperator | double applyAsInt(double left, double right) |
Consumer
表示一个方法接收参数但不产生返回值。
接收一个参数
interface | functional method | 说明 |
---|---|---|
Consumer | void accept(T t) | 接收一个泛型参数,无返回值 |
IntConsumer | void accept(int value) | 以下三个类,接收一个指定类型的参数 |
LongConsumer | void accept(long value) | |
DoubleConsumer | void accept(double value) |
接收两个参数
interface | functional method | 说明 |
---|---|---|
BiConsumer<T,U> | void accept(T t, U u) | 接收两个泛型参数 |
ObjIntConsumer | void accept(T t, int value) | 以下三个类,接收一个泛型参数,一个指定类型的参数 |
ObjLongConsumer | void accept(T t, long value) | |
ObjDoubleConsumer | void accept(T t, double value) |
Supplier
返回一个结果,并不要求每次调用都返回一个新的或者独一的结果
interface | functional method | 说明 |
---|---|---|
Supplier | T get() | 返回类型为泛型T |
BooleanSupplier | boolean getAsBoolean() | 以下三个接口,返回指定类型 |
IntSupplier | int getAsInt() | |
LongSupplier | long getAsLong() | |
DoubleSupplier | double getAsDouble() |
Predicate
根据接收参数进行断言,返回boolean类型
interface | functional method | 说明 |
---|---|---|
Predicate | boolean test(T t) | 接收一个泛型参数 |
IntPredicate | boolean test(int value) | 以下三个接口,接收指定类型的参数 |
LongPredicate | boolean test(long value) | |
DoublePredicate | boolean test(double value) | |
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个泛型参数,分别为T,U |
备注:关于lambda表达式 JDK8以前,通过匿名内部类可以实现接口
Function<Integer, String> fun = new Function<Integer, String>() {
@Override
public String apply(Integer t) {
return String.valueOf(t);
}
};
JDK8中,通过lambda表达式实现,可以得出一个结论,lambda表达式就是为了优化匿名内部类而生。
Function<Integer, String> fun = (x) -> String.valueOf(x);
String res = fun.apply(1000);
System.out.println(res);
接口使用示例
一、Supplier供给型函数式接口
1.1 无参数,返回一个结果。
例:get方法使用
public static String supplierTest(Supplier<String> supplier) {
return supplier.get();
}
public static void main(String args[]) {
String name = "冷冷";
// () -> name.length() 无参数,返回一个结果(字符串长度)
// 所以该lambda表达式可以实现Supplier接口
System.out.println(supplierTest(() -> name.length() + ""));
}
输出:
2
复制
1.2 有参数,返回一个结果
例:生成10个100以内的随机数
//生成num个整数,并存入集合
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
public static void main(String[] args) {
//10个100以内的随机数
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
二、Consumer消费型函数式接口
代表了 接受一个输入参数并且无返回的操作
例:accept方法使用
public static void modifyTheValue3(int value, Consumer<Integer> consumer) {
consumer.accept(value);
}
public static void main(String[] args) {
// (x) -> System.out.println(x * 2)接受一个输入参数x
// 直接输出,并没有返回结果
// 所以该lambda表达式可以实现Consumer接口
modifyTheValue3(3, (x) -> System.out.println(x * 2));
}
输出:
6
三、Function功能型函数式接口
Function接口 接受一个输入参数T,返回一个结果R。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
// 接受输入参数,对输入执行所需操作后 返回一个结果。
R apply(T t);
// 返回一个 pply方法,再执行当前函数对象apply方法的 函数对象。
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 返回一个 先执行当前函数对象apply方法, 再执行after函数对象apply方法的 函数对象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
// 返回一个执行了apply()方法之后只会返回输入参数的函数对象。
static <T> Function<T, T> identity() {
return t -> t;
}
}
例:apply方法使用
/*
Function接口常用于数据的处理转换,比如给定一个员工列表,需要返回名称列表
*/
public class Employee {
private int id;
private String name;
private double salary;
public Employee(String name){
this.name = name;
}
public Employee(String name,double salary) {
this.name = name;
this.salary = salary;
}
//省略getter setter
}
public class TestEmp{
public static <T, R>List<R> map(List<T> list,Function<T, R> fun){
List<R> returnList = new ArrayList<>(list.size());
for (T e : list) {
returnList.add(fun.apply(e));
}
return returnList
}
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(new Employee("老张"),
new Employee("小李"),
new Employee("老王"),
new Employee("小刘"),
new Employee("小胖"));
List<String> nameList = map(employees, (employee -> employee.getName()));
System.out.println(nameList);
/*
console:[老张, 小李, 老王, 小刘, 小胖]
*/
}
}
例:andThen方法使用
public static Integer modifyTheValue2(int value, Function<Integer, Integer> function1, Function<Integer, Integer> function2){
//value作为function1的参数,返回一个结果,该结果作为function2的参数,返回一个最终结果
return function1.andThen(function2).apply(value);
}
public static void main(String[] args) {
System.out.println(modifyTheValue2(3, val -> val + 2, val -> val + 3));
}
四、Predicate断言型函数式接口
接受一个输入参数,返回一个布尔值结果。
例:test方法使用1
public static boolean predicateTest(int value, Predicate<Integer> predicate) {
return predicate.test(value);
}
public static void main(String[] args) {
// (x) -> x == 3 输入一个参数x,进行比较操作,返回一个布尔值
// 所以该lambda表达式可以实现Predicate接口
System.out.println(predicateTest(3, (x) -> x == 3));
}
输出:
true
例:test方法使用2
public static <E> List<E> filter(List<E> list, Predicate<E> pred) {
List<E> retList = new ArrayList<>();
for (E e : list) {
if (pred.test(e)) {
retList.add(e);
}
}
return retList;
}
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(new Employee("老张"),
new Employee("小李", 3000.00),
new Employee("老王", 5000.00),
new Employee("小刘", 7000.00),
new Employee("小胖", 10000.00));
//过滤薪资小于5000的员工
List<Employee> filter = filter(employees,
employee -> employee.getSalary() > 5000.00);
for (Employee employee : filter) {
System.out.println(employee.getName() + ":" + employee.getSalary());
}
/*
console:小刘:7000.0
小胖:10000.0
*/
}
例:test方法使用3
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list) {
if (predicate.test(n)) {
System.out.print(n + " ");
}
}
// list.forEach(n -> {
// if (predicate.test(n)) {
// System.out.print(n + " ");
// }
// });
}
public static void main(String args[]) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有数据:");
// 传递参数 n
eval(list, n -> true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n%2 为 0 test 方法返回 true
System.out.println("\n输出所有偶数:");
eval(list, n -> n % 2 == 0);
// Predicate<Integer> predicate2 = n -> n > 3
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n 大于 3 test 方法返回 true
System.out.println("\n输出大于 3 的所有数字:");
eval(list, n -> n > 3);
}
复制
输出:
输出所有数据:
1 2 3 4 5 6 7 8 9
输出所有偶数:
2 4 6 8
输出大于 3 的所有数字:
4 5 6 7 8 9
复制
例:test方法使用4
public static boolean validInput(String name, Predicate<String> function) {
return function.test(name);
}
public static void main(String args[]) {
String name = "冷冷";
if(validInput(name, s -> !s.isEmpty() && s.length() <= 3 )) {
System.out.println("名字输入正确");
}
}
复制
相关新特性
方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!方法引用:使用操作符 ::
将方法名和对象或类的名字分隔开来。如下三种主要使用情况
对象 : : 实例方法 类 : : 静态方法 类 : : 实例方法 基本用法 例如:
//静态方法
BinaryOperator<Double> binaryOperator = (x, y) -> Math.pow(x, y);
//等价于
BinaryOperator<Double> binaryOperator = Math::pow;
复制
//实例方法: 类::实例方法
Function<Employee, String> f = (Employee e) -> e.getName();
//等价于
Function<Employee, String> f = Employee::getName;
//---------------------------------------------------------
//对象::实例方法
Employee e = new Employee("小李", 3000.00);
Supplier<String> s = () -> e.getName();
//等价于↓
Supplier<String> s = e::getName;
复制
构造方法
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数 列表要与接口中抽象方法的参数列表一致!对于构造方法,方法引用的语法是<类名>::new,如Employee::new
,如下语句:
Function<String,Employee> f = (name)->new Employee(name);
//等价于↓
Function<String, Employee> f = Employee::new;
复制
接口中的默认方法和静态方法
Java8以前,接口里的方法要求全部是抽象方法,Java8以后允许在接口里定义默认方法和静态方法,默认方法使用 default 关键字修饰。默认方法的主要优势是提供一种拓展接口的方法,而不破坏现有代码。
例如:
public interface MyFunction{
void func();
//声明一个接口的默认方法
default void testDefalut(){
System.out.println("MyFunction 默认方法");
}
//声明一个接口的静态方法
static void testStatic(){
System.out.println("MyFunction 静态方法");
}
}
复制
//MyFunctionImpl实现接口MyFunction
public class MyFunctionImpl implements MyFunction {
@Override
public void func() {
System.out.println("实现抽象方法");
}
public static void main(String[] args) {
MyFunction my = new MyFunctionImpl();
my.func();
my.testDefalut();
MyFunction.testStatic();
}
/*
实现抽象方法
MyFunction 默认方法
MyFunction 静态方法
*/
}
复制
接口冲突
如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
public interface AnotherFunction {
default void testDefalut() {
System.out.println("AnotherFunction 默认方法");
}
}
public class FunctionImpl implements MyFunction,AnotherFunction{
@Override
public void func() {
System.out.println(" FunctionImpl 实现抽象方法");
}
@Override
public void testDefalut() {
System.out.println(" FunctionImpl 覆盖接口中默认方法解决冲突");
}
}
复制
如果不覆盖接口中相同的默认方法,那么new MyFunctionImpl().testDefalut();
中调用的testDefalut方法到底是哪个接口的testDefalut()方法呢?所以必须在实现类中覆盖testDefalut()方法。