Java 8 - 03 Lambda 函数式接口Predicate & Consumer & Function & Supplier

简介: Java 8 - 03 Lambda 函数式接口Predicate & Consumer & Function & Supplier

20200510181139786.png

Pre


Java 8 - 02 Lambda Expression中我们讨论了函数式接口, 函数式接口定义且只定义了一个抽象方法。因为抽象方法的签名可以描述Lambda表达式的签名。函数式接口的抽象方法的签名称为函数描述符。


所以为了应用不同的Lambda表达式,我们需要一套能够描述常见函数描述符的函数式接口Java API中已经有了几个函数式接口,比如 Comparable 、 Runnable 和Callable 。


Java 8 在 java.util.function 包中引入了几个新的函数式接口,比比较常用的Predicate 、 Consumer 和 Function 等 。


Predicate 断言型函数式接口


20200516090258393.png

package java.util.function;
import java.util.Objects;
/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@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);
    }
}


标注了@FunctionalInterface , 抽象方法只能包含一个 , default 方法 和 static的方法除外 , 比如 Predicate ,只有test是抽象方法,其他的几个都是default级别和static修饰的方法,所以Predicate也是一个函数式接口 。


java.util.function.Predicate<T> 接口定义了一个名叫 test 的抽象方法,它接受泛型T 对象,并返回一个 boolean 。


如果需要表示一个涉及类型 T 的布尔表达式时,就可以使用这个接口


举个例子

import com.artisan.domain.Enginner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
 * @author 小工匠
 * @version v1.0
 * @create 2020-05-16 9:13
 * @motto show me the code ,change the word
 * @blog https://artisan.blog.csdn.net/
 * @description
 **/
public class PredicateDemo {
    /**
     * 过滤符合规则的泛型类
     * @param list
     * @param predicate
     * @param <T>
     * @return
     */
    private static <T> List<T> filter(List<T> list , Predicate<T> predicate){
        List<T> targetList = new ArrayList<>();
        for (T t :list){
            if (predicate.test(t)){
                targetList.add(t);
            }
        }
        return  targetList;
    }
     public static void main(String[] args) {
        List<Enginner> enginnerList = Arrays.asList(new Enginner("Java", 18), new Enginner("GO", 20));
        List<Enginner> goEngineerList = filter(enginnerList, enginner -> enginner.getJob().equals("GO"));
        System.out.println(goEngineerList);
    }
}

我们通过 filter(enginnerList, (enginner) -> enginner.getJob().equals("GO")); 第二个参数 (enginner) -> enginner.getJob().equals("GO") 方法签名返回的是Boolean,所以可以使用Predicate这个JDK8中提供的接口 。


当然了你也可以直接写一个类似Predicate的函数式接口来供自己调用 ,如下

@FunctionalInterface
public interface Filter<T> {
    boolean filter(T t);
}

那么就变成了如下的样子

    private static <T> List<T> filterCustom(List<T> list , Filter<T> filter){
        List<T> targetList = new ArrayList<>();
        for (T t :list){
            if (filter.filter(t)){
                targetList.add(t);
            }
        }
        return  targetList;
    }


main方法中调用如下

 List<Enginner> javaEngineerList= filterCustom(enginnerList, enginner -> enginner.getJob().equals("Java"));
System.out.println(javaEngineerList);


20200516093159105.png


Consumer 消费型函数式接口


20200516201713324.png


package java.util.function;
import java.util.Objects;
/**
 * 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.
 *
 * @since 1.8
 */
@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); };
    }
}


标注了@FunctionalInterface , 抽象方法只能包含一个 , default 方法 和 static的方法除外 , 比如 Consumer,只有accept是抽象方法Consumer是default级的方法,所以Consumer也是一个函数式接口 。


java.util.function.Consumer<T> 定义了一个名叫 accept 的抽象方法,它接受泛型 T的对象,没有返回( void )。


如果需要访问类型 T 的对象,并对其执行某些操作,就可以使用这个接口


举个例子


比如,你可以用它来创建一个 forEach 方法,接受一个 Integers 的列表,并对其中每个元素执行操作。 假设我们要使用这个 forEach 方法,并配合Lambda来打印列表中的所有元素。

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
/**
 * @author 小工匠
 * @version v1.0
 * @create 2020-05-16 20:20
 * @motto show me the code ,change the word
 * @blog https://artisan.blog.csdn.net/
 * @description
 **/
public class ComusmerDemo {
    public static <T> void doForEach(List<T> tList, Consumer<T> consumer){
        for (T t: tList ) {
            consumer.accept(t);
        }
    }
    public static void main(String[] args) {
        // 第二个参数 Lambda是 Consumer 中accept 方法的实现
        doForEach(Arrays.asList(1,2,3,5,7),(Integer i) -> System.out.println(i));
    }
}

20200516210151238.png


Function 功能型函数式接口


20200516205045829.png


package java.util.function;
import java.util.Objects;
/**
 * 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> {
    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;
    }
}


java.util.function.Function<T, R> 接口定义了一个叫作 apply 的方法,它接受一个泛型 T 的对象,并返回一个泛型 R 的对象。


如果我们需要定义一个Lambda,将输入对象的信息映射到输出,就可以使用这个接口 ,举个例子提取工程师的职位或把字符串映射为它的长度等等


来个小demo : 利用Function 来创建一个 map 方法,以将一个 String 列表映射到包含每个

String 长度的 Integer 列表

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
 * @author 小工匠
 * @version v1.0
 * @create 2020-05-16 20:47
 * @motto show me the code ,change the word
 * @blog https://artisan.blog.csdn.net/
 * @description
 **/
public class FunctionDemo {
    /**
     * @param list
     * @param function
     * @param <T>
     * @param <R>
     * @return 返回 List<R>
     */
    public static <T, R> List<R> doSomething(List<T> list, Function<T, R> function) {
        List<R> rList = new ArrayList<>();
        for (T t : list) {
            R apply = function.apply(t);
            rList.add(apply);
        }
        return rList;
    }
    public static void main(String[] args) {
        // Lambda是 Function 接口的 apply 方法的 实现
        List<Integer> list = doSomething(Arrays.asList("artisan", "small", "happy"), (String s) -> s.length());
        System.out.println(list);
    }
}

20200516210131980.png


Supplier 供给型函数式接口

20200516211144618.png


package java.util.function;
/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 * 
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}


无参数,返回一个结果。

import com.artisan.domain.Enginner;
import java.util.function.Supplier;
/**
 * @author 小工匠
 * @version v1.0
 * @create 2020-05-16 21:08
 * @motto show me the code ,change the word
 * @blog https://artisan.blog.csdn.net/
 * @description
 **/
public class SupplierDemo {
    public static <T> T doGet(Supplier<T> supplier) {
        return supplier.get();
    }
    public static void main(String[] args) {
        Enginner enginneer = new Enginner("JAVA", 18);
        String s = doGet(() -> enginneer.getJob());
        System.out.println(s);
    }
}


2020051621174739.png

小结


我们介绍了4个泛型函数式接口: Predicate<T> 、 Consumer<T> 、Function<T,R> 、Supplier<T>


还有些函数式接口专为某些类型而设计。


回顾一下:Java类型要么是引用类型(比如 Byte 、 Integer 、 Object 、 List ),要么是原始类型(比如 int 、 double 、 byte 、 char ).


但是泛型(比如 Consumer<T> 中的 T )只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing).


Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。


比如,这就是为什么下面的代码是有效的(一个 int 被装箱成为Integer )

List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100; i++){
  list.add(i);
}

但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。


Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。


比如,在下面的代码中,使用 IntPredicate 就避免了对值 1000 进行装箱操作,但要是用 Predicate<Integer> 就会把参数 1000 装箱到一个 Integer 对象中:

20200516212246329.png


public class IntPredicateDemo {
    public static void main(String[] args) {
        // 无装箱
        IntPredicate intPredicate = (int i) -> i % 2 == 0;
        intPredicate.test(1000);
        // 装箱
        Predicate<Integer> predicate = (Integer i) -> i % 2 == 0;
        predicate.test(1000);
    }
}


一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如 DoublePredicate 、 IntConsumer 、 LongBinaryOperator 、 IntFunction 等。


Function接口还有针对输出参数类型的变种: ToIntFunction<T> 、 IntToDoubleFunction 等

20200516213553115.png

20200516213623205.png

来个小测验

请构造一个可以利用这些函数式接口的有效Lambda
表达式:
(1)  T->R
(2)  (int, int)->int
(3)  T->void
(4)  ()->T
(5)  (T, U)->R
(1)  Function<T,R> 不错。它一般用于将类型 T 的对象转换为类型 R 的对象(比如
Function<Apple, Integer> 用来提取苹果的重量)。
(2)  IntBinaryOperator 具有唯一一个抽象方法,叫作 applyAsInt ,它代表的函数描述
符是 (int, int) -> int 。
(3)  Consumer<T> 具有唯一一个抽象方法叫作 accept ,代表的函数描述符是 T -> void 。
(4)  Supplier<T> 具有唯一一个抽象方法叫作 get ,代表的函数描述符是 ()-> T 。或者,
Callable<T> 具有唯一一个抽象方法叫作 call ,代表的函数描述符是 () -> T 。
(5)  BiFunction<T, U, R> 具有唯一一个抽象方法叫作 apply ,代表的函数描述符是 (T,
U) -> R 。

最后 总结关于函数式接口和Lambda

20200516214051854.png


函数式接口如何处理异常信息


Note : ,任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个 try/catch 块中。

自己的函数式接口如下:


@FunctionalInterface
public interface BufferedReaderProcessor {
  String process(BufferedReader b) throws IOException;
}

调用

BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();


但是你可能是在使用一个接受函数式接口的API,比如 Function<T, R> ,没有办法自己创建一个。这种情况下, 可以显式捕捉受检异常:

Function<BufferedReader, String> f = (BufferedReader b) -> {
try {
  return b.readLine();
}
catch(IOException e) {
  throw new RuntimeException(e);
  }
};


相关文章
|
24天前
|
Java 数据处理
|
2月前
|
设计模式 Java
结合HashMap与Java 8的Function和Optional消除ifelse判断
`shigen`是一位致力于记录成长、分享认知和留住感动的博客作者。本文通过具体代码示例探讨了如何优化业务代码中的if-else结构。首先展示了一个典型的if-else处理方法,并指出其弊端;然后引入了策略模式和工厂方法等优化方案,最终利用Java 8的Function和Optional特性简化代码。此外,还提到了其他几种消除if-else的方法,如switch-case、枚举行、SpringBoot的IOC等。一起跟随shigen的脚步,让每一天都有所不同!
35 10
结合HashMap与Java 8的Function和Optional消除ifelse判断
|
4月前
|
并行计算 Java API
深入理解Java中的Lambda表达式与函数式接口
【7月更文挑战第19天】在Java 8中引入的Lambda表达式,不仅简化了代码编写,还为函数式编程提供了支持。本文将探讨Lambda表达式的核心概念、其与函数式接口的关系以及如何在Java中高效利用这一特性来提升代码的简洁性和可读性。我们将通过实例分析Lambda表达式的语法规则和常见用法,同时解释函数式接口的设计原则及其在Java标准库中的应用,旨在帮助开发者更好地理解和运用这一强大的工具。
|
3月前
|
消息中间件 Java Kafka
"Kafka快速上手:从环境搭建到Java Producer与Consumer实战,轻松掌握分布式流处理平台"
【8月更文挑战第10天】Apache Kafka作为分布式流处理平台的领头羊,凭借其高吞吐量、可扩展性和容错性,在大数据处理、实时日志收集及消息队列领域表现卓越。初学者需掌握Kafka基本概念与操作。Kafka的核心组件包括Producer(生产者)、Broker(服务器)和Consumer(消费者)。Producer发送消息到Topic,Broker负责存储与转发,Consumer则读取这些消息。首先确保已安装Java和Kafka,并启动服务。接着可通过命令行创建Topic,并使用提供的Java API实现Producer发送消息和Consumer读取消息的功能。
71 8
|
3月前
|
消息中间件 Java 大数据
"深入理解Kafka单线程Consumer:核心参数配置、Java实现与实战指南"
【8月更文挑战第10天】在大数据领域,Apache Kafka以高吞吐和可扩展性成为主流数据流处理平台。Kafka的单线程Consumer因其实现简单且易于管理而在多种场景中受到欢迎。本文解析单线程Consumer的工作机制,强调其在错误处理和状态管理方面的优势,并通过详细参数说明及示例代码展示如何有效地使用KafkaConsumer类。了解这些内容将帮助开发者优化实时数据处理系统的性能与可靠性。
89 7
|
3月前
|
消息中间件 负载均衡 Java
"深入Kafka核心:探索高效灵活的Consumer机制,以Java示例展示数据流的优雅消费之道"
【8月更文挑战第10天】在大数据领域,Apache Kafka凭借其出色的性能成为消息传递与流处理的首选工具。Kafka Consumer作为关键组件,负责优雅地从集群中提取并处理数据。它支持消息的负载均衡与容错,通过Consumer Group实现消息的水平扩展。下面通过一个Java示例展示如何启动Consumer并消费数据,同时体现了Kafka Consumer设计的灵活性与高效性,使其成为复杂消费场景的理想选择。
120 4
|
3月前
|
安全 Oracle Java
【Azure Function】Azure Function中使用 Java 8 的安全性问题
【Azure Function】Azure Function中使用 Java 8 的安全性问题
|
4月前
|
Java 开发者
Java中的Lambda表达式与函数式接口
【7月更文挑战第20天】本文深入探讨Java 8引入的Lambda表达式及其在函数式编程中的应用。我们将分析Lambda表达式如何简化代码、提高可读性,以及它与传统匿名内部类的区别。文章还将介绍函数式接口的概念,并通过实际示例展示如何利用Lambda表达式和函数式接口优化Java代码。
|
4月前
|
并行计算 Java 大数据
Java中的函数式接口与流式编程
Java中的函数式接口与流式编程

热门文章

最新文章