Java8新特性之函数式接口-Supplier、Consumer、Predicate、Function详解

1. 概述

函数式接口在Java中是指:有且仅有一个抽象方法的接口

Lambda表达式是用来实现SAM接口的,所谓SAM就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。

从应用层面来讲,Java中的Lambda表达式可以看做是接口对应的匿名内部类的简化格式,但是二者在原理上不同。

@FunctionalInterface注解

@Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface,该注解可用于一个接口的定义上:

@FunctionalInterface
public interface MyFunctionalInterface {
	void myMethod();
}

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。不过,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

JDK1.8之前,核心类库中就已经存在很多SAM接口了,例如:

(1)java.lang.Runnable

(2)java.util.concurrent.Callable

(3)java.util.Comparator

(4)java.lang.Comparable

(5)java.lang.Iterable

(6)java.io.FileFilter

(7)java.lang.reflect.InvocationHandler

…等

但是在JDK1.8,只有部分方法加了 @FunctionalInterface,那些没有加 @FunctionalInterface 的SAM接口,存在将来增加抽象方法变成非SAM接口的风险,因此建议只对加了 @FunctionalInterface 的接口使用Lambda表达式实现。
虽然在JDK1.8,Comparator接口中新增了一个抽象方法boolean equals(Object obj),但它仍是函数式接口,在接口声明中也加了@FunctionalInterface 注解,因为关于FunctionalInterface注解,官方文档是这样说的:

If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
如果接口中声明的抽象方法覆盖了java.lang.Object的公共方法,则该方法不会计入接口的抽象方法计数,因为该接口的任何实现都将具有java.lang.Object或其他地方的实现。

JDK1.8在 java.util.function 包增加了很多函数式接口,不过他们可以归纳为四类:供给型接口、消费型接口、功能型接口、判断型接口,一共43个

2. 供给型接口

这类接口名以单词Supplier结尾,Supplier:供应者。对应的Lambda表达式需要“对外提供”数据。

这类接口的抽象方法特点:无参,有返回值;

接口名抽象方法描述
SupplierT get()返回一个对象
BooleanSupplierboolean getAsBoolean()返回一个boolean值
DoubleSupplierdouble getAsDouble()返回一个double值
IntSupplierint getAsInt()返回一个int值
LongSupplierlong getAsLong()返回一个long值

典型代表—Supplier接口

java.util.function.Supplier<T>接口,对应的Lambda表达式需要对外提供一个符合泛型类型的对象数据。接口定义:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

抽象方法 : T get()

用来获取一个泛型参数指定类型的对象数据。

代码示例:

public class SupplierDemo {
    public static void main(String[] args) {
        String strA = "Hello ";
        String strB = "World";
        
        //传参:Supplier<String> supplier = () -> strA + strB; 
        //调用重写的get方法:get() {return strA + strB;}
        String result = getString(() -> strA + strB);
        System.out.println(result);
    }

    static String getString(Supplier<String> supplier) {
        return supplier.get();
    }
}

求数组元素最大值

使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值,接口的泛型使用java.lang.Integer类。

代码示例:

public class IntArrayTest {
    public static void main(String[] args) {
        int[] array = {10, 5, 12, 3, 7, 15};
        pringMax(() -> {
            int max = array[0];
            for (int i = 1; i < array.length; i++) {
                if (array[i] > max) {
                    max = array[i];
                }
            }
            return max;
        });
    }

    static void pringMax(Supplier<Integer> supplier) {
        int max = supplier.get();
        System.out.println(max);
    }
}

3. 消费型接口

这类接口名称以单词Consumer结尾,Consumer:消费者。对应的Lambda表达式需要接收参数来完成功能。

这类接口的抽象方法特点:有参、无返回值

接口名抽象方法描述
Consumervoid accept(T t)接收一个对象用于完成功能
BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
DoubleConsumervoid accept(double value)接收一个double值
IntConsumervoid accept(int value)接收一个int值
LongConsumervoid accept(long value)接收一个long值
ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值
ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值
ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值

典型代表—Consumer接口

java.util.function.Consumer<T> :接收一个对象,来执行相关操作,其数据类型由泛型参数决定。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

抽象方法:void accept(T t)

接收一个对象,来执行相关操作

public class ConsumerDemo {
    public static void main(String[] args) {
        String str = "Hello world";
        //字符串转为小写
        convert(s -> System.out.println(s.toLowerCase()), str);
    }

    static void convert(Consumer<String> consumer, String str) {
        consumer.accept(str);
    }
}

默认方法:andThen,如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以:接收一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。源码:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

代码示例:大小写转换

public class ConsumerDemo {
    public static void main(String[] args) {
        String str = "Hello world";
        //Consumer<String> c1 = s1 -> System.out.println(s1.toLowerCase());
        //Consumer<String> c2 = s2 -> System.out.println(s2.toUpperCase());
        convert(s1 -> System.out.println(s1.toLowerCase()),
                s2 -> System.out.println(s2.toUpperCase()), str);
    }

    static void convert(Consumer<String> c1, Consumer<String> c2, String str) {
        c1.accept(str);
        c2.accept(str);
        //andThen方法返回值为Consumer<T>接口的实现类对象:(T t) -> { accept(t); after.accept(t); }
        //先小写后大写:c1.accept(str); c2.accept(str);
        c1.andThen(c2).accept(str);
        //先大写后小写:c2.accept(str); c1.accept(str)
        c2.andThen(c1).accept(str);
    }
}

上面代码中,c1.andThen(c2) 返回Consumer接口的实现类对象,该对象调用accept(str),此时重写的accept(str)方法的方法体为{c1.accept(str); c2.accept(str)}

4. 判断型接口

这类接口名称以单词Predicate结尾,Predicate:断言、判断。对某种类型的数据进行判断,从而得到一个boolean值结果。

这类接口的抽象方法特点:有参、返回值类型是boolean

接口名抽象方法描述
Predicateboolean test(T t)接收一个对象
BiPredicate<T,U>boolean test(T t, U u)接收两个对象
DoublePredicateboolean test(double value)接收一个double值
IntPredicateboolean test(int value)接收一个int值
LongPredicateboolean test(long value)接收一个long值

典型代表—Predicate接口

接口定义:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
	//实现“与逻辑”方法
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
	//实现“非逻辑”方法
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
	//实现“或逻辑”方法
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
	//两个对象的equals方法比较
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

抽象方法:boolean test(T t),用于条件判断的场景,条件判断的标准是传入的Lambda表达式逻辑

代码示例:

输出正数数组中的所有偶数:

public class PredicateTest {
    public static void main(String[] args) {
        int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
        System.out.println("所有偶数:");
        eval(array, n -> n%2 == 0);
    }

    static void eval(int[] array, Predicate<Integer> p) {
        for (int i : array) {
            if (p.test(i)) {
                System.out.println(i);
            }
        }
    }
}

默认方法:and,既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法 and ,源码:

default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}

输出大于等于6所有偶数:

public class PredicateTest {
    public static void main(String[] args) {
        int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
        System.out.println("大于等于6所有偶数:");
        eval(array, m -> m >= 6, n -> n%2 == 0);
    }

    static void eval(int[] array, Predicate<Integer> p1, Predicate<Integer> p2) {
        for (int i : array) {
            if (p1.and(p2).test(i)) {
                System.out.println(i);
            }
        }
    }
}

默认方法:or,与 and 的“与”类似,默认方法 or 实现逻辑关系中的,源码:

default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}

输出大于6的整数或偶数:

public class PredicateTest {
    public static void main(String[] args) {
        int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
        System.out.println("大于6的整数或偶数:");
        eval(array, m -> m >= 6, n -> n%2 == 0);
    }

    static void eval(int[] array, Predicate<Integer> p1, Predicate<Integer> p2) {
        for (int i : array) {
            if (p1.or(p2).test(i)) {
                System.out.println(i);
            }
        }
    }
}

默认方法:negate,实现逻辑关系中的,源码:

default Predicate<T> negate() {
    return (t) -> !test(t);
}

输出不大于7的整数:

public class PredicateTest {
    public static void main(String[] args) {
        int[] array = {0, 1, 2, 3, 4, 6, 7, 10, 15, 20};
        System.out.println("不大于7的整数:");
        eval(array, n -> n > 7);
    }

    static void eval(int[] array, Predicate<Integer> p) {
        for (int i : array) {
            if (p.negate().test(i)) {
                System.out.println(i);
            }
        }
    }
}

5. 功能型接口

这类接口名称以单词Function或Operator结尾,用于完成某项功能并返回数据

这类接口的抽象方法特点:有参数、有返回值

接口名抽象方法描述
Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果
UnaryOperatorT apply(T t)接收一个T类型对象,返回一个T类型对象结果
DoubleFunctionR apply(double value)接收一个double值,返回一个R类型对象
IntFunctionR apply(int value)接收一个int值,返回一个R类型对象
LongFunctionR apply(long value)接收一个long值,返回一个R类型对象
ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象,返回一个double
ToIntFunctionint applyAsInt(T value)接收一个T类型对象,返回一个int值
ToLongFunctionlong applyAsLong(T value)接收一个T类型对象,返回一个long值
DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int值
DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long值
IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double值
IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long值
LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double值
LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int值
DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double值
IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int值
LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long值
BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象
BinaryOperatorT apply(T t, T u)接收两个T类型对象,返回一个T类型对象
ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double值
ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int值
ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long值
DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double值
IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int值
LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long值

典型代表—Function接口

java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据

接口定义:

@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;
    }
}

抽象方法:R apply(T t)

Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。

类似于数学中的函数:
y = f ( x ) y = f(x) y=f(x)
代码演示:将String类型转换为Integer类型。

public class FuntionTest {
    public static void main(String[] args) {
        parse(s -> Integer.parseInt(s), "10");
    }

    static void parse(Function<String, Integer> func , String str) {
        int num =  func.apply(str);
        System.out.println(num);
    }
}

默认方法:compose,源码:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

compose方法接收一个Function类型的参数,先用传入的Function对象执行对应的apply方法,然后使用当前的Function对象的执行对应的apply方法

代码演示:先将字符串与字符’0’拼接,再转为整数

public class FuntionTest {
    public static void main(String[] args) {
        parse(s1 -> Integer.parseInt(s1), s2 -> s2 + '0', "10");
    }

    static void parse(Function<String, Integer> func1, Function<String, String> func2 , String str) {
        //先用传入的 func2 = s2 -> s2 + '0'; 执行apply
        //然后用当前的 func1 = s1 -> Integer.parseInt(s1); 执行apply
        int num = func1.compose(func2).apply(str);
        System.out.println(num);
    }
}

默认方法:andThen,源码:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

andThen方法接收一个Function类型的参数,先用当前的Function对象的执行对应的apply方法,然后使用传入的Function对象执行对应的apply方法

代码演示:先将字符串转为整数,然后将此整数乘以10

public class FuntionTest {

    public static void main(String[] args) {
        parse(s -> Integer.parseInt(s), i -> i * 12, "10");
    }

    static void parse(Function<String, Integer> func1, Function<Integer, Integer> func2 , String str) {
        //先用当前的 func1 = s1 -> Integer.parseInt(s1); 执行apply
        //然后用传入的 func2 = i -> i * 12; 执行apply
        int num = func1.andThen(func2).apply(str);
        System.out.println(num);
    }
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值