Java 函数式编程(常用接口)

        之前已经介绍过了Java8函数式变成及Lambda表达式,感兴趣可以看看,地址:Java8函数式编程(Lambda表达式)_琅琊之榜PJ的博客-优快云博客

 本文主要介绍一下常用的接口及用法,先来看一个表格:

 本文主要选取几个常用的:Supplier<T>、Consumer<T>、Function<T,R>、Predicate<T>

在此之前,再来了解一下@FunctionalInterface注解。

一、@FunctionalInterface注解

        一种信息注释类型,用于指示接口类型声明是Java语言规范定义的函数接口。从概念上讲,函数接口只有一个抽象方法。由于默认方法有一个实现,所以它们不是抽象的。如果一个接口声明了一个覆盖java.lang.Object的一个公共方法的抽象方法,那么这也不计入该接口的抽象方法计数,因为该接口的任何实现都将具有来自java.lang.Oobject或其他地方的实现。

        注意,函数接口的实例可以使用lambda表达式、方法引用或构造函数引用创建。

        如果使用此注解类型对类进行注解,则编译器需要生成错误消息,除非类型是接口类型,而不是注解类型、枚举或类。

        带注解的类型满足功能接口的要求。

        但是,无论接口声明中是否存在FunctionalInterface注释,编译器都会将符合函数接口定义的任何接口视为函数接口。

源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

需要注意的是:函数式接口只有一个抽象方法,接口声明覆盖 java.lang.Object的抽象方法,不会进行抽象方法计数。这对后面介绍几个常用的接口很有用!!

二、Supplier<T>

        先看一下源码:

/**
  * 表示结果的供应者。
  * 没有要求每次调用供应者时都返回新的或不同的结果。
  */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     */
    T get();
}

        Supplier<T> 接口源码中只有个get()方法。每次调用get()方法,就会调用构造方法,获取对象实例。

        示例:每次调用get()方法都返回一个整数:

        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return new Random().nextInt();
            }
        };
        System.out.println(supplier.get());

        结合Lambda表达式应用:

    List<Integer> integers = makeList(10, () -> (int) (Math.random() * 10));
    integers.forEach(System.out::println);
 
    private List<Integer> makeList(int num, Supplier<Integer> supplier){
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(supplier.get());
        }
        return list;
    }

        结合方法引用应用:

Supplier<Double> supplier1 = Math::random;
System.out.println(supplier1.get());

三、Consumer<T>

Java源码:

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

        Consumer<T>表示接受单个输入参数而不返回结果的操作。java.util.function.Consumer 接口则正好与上面的Supplier<T> 接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。

该类有两个方法

  • void accept(T t);【抽象方法】

        该方法对给定的参数执行此操作。

  • default Consumer<T> andThen(Consumer<? super T> after)【默认实现方法】

        返回一个组合的Consumer,该Consumer依次执行此操作和after操作。如果执行任何一个操作都会引发异常,则会将异常中继到组合操作的调用方。如果执行此操作会引发异常,则不会执行after操作。

1、抽象方法accept的使用

该方法消费一个指定泛型的数据:

import java.util.function.Consumer;
 
public class Demo01Consumer {
    public static void main(String[] args) {
        consumerString(s -> System.out.println(s));
    }
 
    private static void consumerString(Consumer<String> function) {
        function.accept("Hello");
    }
}

运行程序,控制台输出:Hello

2、默认方法andThen的使用

        如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。

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

提示:java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出 NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

        要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:

import java.util.function.Consumer;
 
public class Demo02Consumer {
    public static void main(String[] args) {
        consumerString(
                // toUpperCase()方法,将字符串转换为大写
                s -> System.out.println(s.toUpperCase()),
                // toLowerCase()方法,将字符串转换为小写
                s -> System.out.println(s.toLowerCase())
        );
    }
 
    private static void consumerString(Consumer<String> one, Consumer<String> two) {
        one.andThen(two).accept("Hello");
    }
}

        运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

四、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;
    }
}

Function<T,R>接口的作用的是用来表现一个参数类型为T、返回值类型为R的函数(方法)。

Function 接口是一个功能型接口,是一个转换数据的作用。接收一个T参数,返回一个R结果

Function 接口实现 apply 方法来做转换。

一个简单的示例:

Function<String, Boolean> function = p -> p.length() == 5;
Stream<Boolean> stream = stringList().stream().map(function);

默认方法1:<V> Function<V, R> compose(Function<? super V, ? extends T> before)

返回一个组合函数,该函数首先将before函数应用于其输入,然后将此函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。

默认方法2:<V> Function<T, V> andThen(Function<? super R, ? extends V> after)

返回一个组合函数,该函数首先将此函数应用于其输入,然后将after函数应用于结果。如果对任意一个函数的求值引发异常,则会将异常中继到组合函数的调用方。

静态方法:static <T> Function<T, T> identity()

返回一个始终返回其输入参数的函数。

五、Predicate<T>

源码:

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


    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

Predicate<T>常用的四个方法:
boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与
default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或
Predicate<T>接口常用于判断参数是否满足指定的条件

最常见的应用是在集合Stream流过滤条件中使用:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
 
 
public class LambdaStudy
{
    public static void main(String[] args) {
       
        //初始化list集合
        List<String> list = new ArrayList<String>();
        list.add("测试数据1");
        list.add("测试数据2");
        list.add("测试数据3");
        list.add("测试数据12");
        
        //使用λ表达式遍历集合
        list.forEach(s -> System.out.println(s));
        
        //结合Predicate使用和过滤条件筛选元素
        Predicate<String> contain1 = n -> n.contains("1");
        Predicate<String> contain2 = n -> n.contains("2");
        
        //根据条件遍历集合
        list.stream().filter(contain1).forEach(n -> System.out.println(n));
        list.stream().filter(s -> contain1.test(s)).forEach(s -> System.out.println(s));
        list.stream().filter(contain1.and(contain2)).forEach(n -> System.out.println(n));
        list.stream().filter(contain1.or(contain2)).forEach(n -> System.out.println(n));
        
        //将过滤后的元素重新放到一个集合中
        List<String> newList = list.stream().filter(contain1.and(contain2)).collect(Collectors.toList());
        
        newList.forEach(s -> System.out.println(s));
 
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值