首先,我们先定义一个函数式编程接口

@FunctionalInterface
public interface BooleanFunctionalInterface<T> {
boolean test(T t);
}

很简单,该接口的唯一一个抽象方法(并且非Object类的方法)返回值为boolean

下面,定义一个方法,接受一个List,利用实现了该接口的test方法的对象,筛选出需要的元素:

import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate; public class Filter<T> {
public static <T> List<T> filter(List<T> list, BooleanFunctionalInterface b){
if (CollectionUtils.isEmpty(list)){
return new ArrayList<>(0);
} List<T> result = new ArrayList<>(list.size());
for(int i=0; i<list.size(); i++){
T t = list.get(i);
if (b.test(t)) {
result.add(t);
}
} return result;
}
}

测试类,筛选出年龄大于25的People对象:

public class FunctionalInterfaceTest {
private List<People> peopleList = new ArrayList<>();
@Before
public void init(){
peopleList.add(new People("LuoTianyan",23));
peopleList.add(new People("ff",26));
peopleList.add(new People("Tony",33));
}
/**
* 自定义函数式接口
*/
@Test
public void testUserDefined(){
List<People> filter = Filter.filter(peopleList, p -> ((People) p).getAge() > 25);
filter.forEach(System.out::println);
/*People(name=ff, age=26)
People(name=Tony, age=33)*/
}
}
import lombok.*;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class People {
private String name;
private int age;
}

JDK中已有的函数式接口

上面的自定义的函数式接口,返回boolean,其实在Java8中已经有该类型的接口,那就是Predicate。

Predicate<T> 接口

该接口定义了一个支持泛型的boolean test( T)的抽象方法,其函数描述符为 (T)-> boolean,现在我们就可以直接使用Predicate<T>接口来替代上面自定义的接口了。

在上面的Filter类中追加一个方法,修改这里形参为Predicate。

public static <T> List<T> predicate(List<T> list, Predicate predicate){
if (CollectionUtils.isEmpty(list)){
return new ArrayList<>(0);
} List<T> result = new ArrayList<>(list.size());
for(int i=0; i<list.size(); i++){
T t = list.get(i);
if (predicate.test(t)) {
result.add(t);
}
} return result;
}

获取Age>25的People对象,测试如下:

/**
* Java8内置的函数式编程接口Predicate,返回boolean类型
*/
@Test
public void testPredicate(){
List<People> predicate = Filter.predicate(peopleList, p -> ((People) p).getAge() > 25);
predicate.forEach(System.out::println);
}

Consumer<T>接口

该接口定义了一个void accept(T)的抽象方法,其函数描述符为 (T) -> void,如果你需要一个操作某一对象,但无需返回的的函数式接口,那么就可以使用Consumer<T>接口。

追加方法,形参为Consumer:

public static <T> void consumer(List<T> list, Consumer consumer){
if (CollectionUtils.isEmpty(list)){
return ;
} List<T> result = new ArrayList<>(list.size());
for(int i=0; i<list.size(); i++){
T t = list.get(i);
consumer.accept(t);
} }

下面实现修改所有Age为18,并且输出,测试如下:

/**
* Java8内置的函数式编程接口Consumer,直接消费无返回值
*/
@Test
public void testConsumer(){
// setAge操作不需要返回值
Consumer<People> setAgeConsumer = p -> ((People) p).setAge(18);
Filter.consumer(peopleList, setAgeConsumer); // 输出操作不需要返回值
Consumer<People> sout = p -> System.out.println((People)p);
Filter.consumer(peopleList, sout);
/*People(name=LuoTianyan, age=18)
People(name=ff, age=18)
People(name=Tony, age=18)*/
}

Supplier<T>接口

既然有消费者接口(Consumer<T>),那就要有生产者接口(Supplier<T>),该接口定义了一个 T get() 的抽象方法,其函数描述符为 () -> T,如果不接受入参,直接为我们生产一个指定的结果,那么就可以用Supplier<T>。

追加方法,形参Supplier

public static <T> List<T> listFactory(int count, Supplier<T> supplier){

        List<T> result = new ArrayList<>(count);
for(int i=0; i<count; i++){
T t = supplier.get();
result.add(t);
} return result;
}

下面生成count个对象,设置对象默认属性值:

/**
* Java8内置的函数式编程接口supplier,无形参,返回对象
*/
@Test
public void testSupplier(){
// 生成对象
Supplier<People> peopleSupplier = () -> new People("init",18);
List<People> people = Filter.listFactory(5, peopleSupplier); // 输出操作不需要返回值
Consumer<People> sout = p -> System.out.println((People)p);
Filter.consumer(people, sout);
/*People(name=init, age=18)
People(name=init, age=18)
People(name=init, age=18)
People(name=init, age=18)
People(name=init, age=18)*/
}

Function<T,R>接口

该接口定义了一个 R apply(T)类型的抽象函数,它接受一个泛型变量T,并返回一个泛型变量R,如果你需要将一个对象T映射成R,那么就可以使用Function<T,R>接口。

下面,我们将对象转化为String类型的例子

public static <T> List<String> function(List<T> list, Function<T,String> function){
if (CollectionUtils.isEmpty(list)){
return new ArrayList<>(0);
} List<String> result = new ArrayList<>(list.size());
for(int i=0; i<list.size(); i++){
T t = list.get(i);
String apply = function.apply(t);
result.add(apply);
} return result;
}
将People对象,转换为的字符串输出:
/**
* Java8内置的函数式编程接口Function,接受形参T,转换为对象R
*/
@Test
public void testFunction(){
// 将People对象,转换为如下形式的字符串
Function<People, String> function = (People p) -> "name:" + p.getName() + " , age:" + p.getAge();
List<String> strings = Filter.function(peopleList, function); // 输出操作不需要返回值
Consumer<String> sout = p -> System.out.println(p);
Filter.consumer(strings, sout);
/*name:LuoTianyan , age:23
name:ff , age:26
name:Tony , age:33*/
}

上面代码的优化

由于上面的people是list集合,可以直接利用stream的形式;

比如People对象转换成字符串

@Test
public void testListStream(){
List<String> collect = peopleList.stream()
.map(p -> p.getName() + p.getAge())
.collect(Collectors.toList()); collect.forEach(System.out::println);
/*LuoTianyan23
ff26
Tony33*/
}

Java 8 中函数式接口列表

现在我们给出一份较为全的函数式接口与描述符对应的接口声明列表:

    函数式接口     函数描述符
Predicate<T>   (T)  -> boolean
Consumer<T>   (T)  -> void
Function< T, R >   (T)  -> R
Supplier<T>   ( )  -> T
UnaryOperator<T>    (T)  ->  T
BinaryOperator<T>   (T, T) -> T
BiPredicate<L, R>   (L, R)  -> boolean
BiConsumer<T, U>   (T, U)  -> void
BiFunction<T, U, R>   (T, U)  -> R

需要的函数式接口没有被覆盖,可以根据JDK中的声明来编写适合自己使用的函数式接口声明。

BiFunction例子

关于装箱与拆箱

泛型的使用使得函数式接口有了更高的灵活性,我觉得这里应该先说一下参数化,参数化是相对于硬编码来说的 ,如我们常用的函数声明具有参数列表,lambda表达式采用了代码参数化技术,泛型则使用了类型参数化技术,参数化是代码走向通用的方法 ,同时也是编程抽象的一种体现。

为了程序员的方便,JDK中提供了现成的支持泛型的函数式接口,但是由于泛型的支持,使得接口也会存在一些性能浪费的问题。我们知道Java泛型只能支持引用类型,也就是对象,不支持原始类型(int、double、char等),在 Java SE5之前Java程序员在泛型中使用原始类型时,只能通过其对应的引用类型(Interger、Double、Charactor)来替换,并且需要使用函数式式的方式进行原始类型到引用类型的转换,如 Integer i = new Integer(10),从 Java SE5开始,Java支持自动装箱拆箱技术,通过赋值操作,便可以将原始类型包装成引用类型如Integer i = 10,相对的自动拆箱便是将引用类型转为原始类型。

但是这样的特性也会带来牺牲性能的代价,装箱的本质是将原始类型包裹起来生成一个对象出来,并将原始类型的值保存到该对象中,相对于原始类型包装的过程和内存的占用都会相应的提高,并且在很多情况下我们使用原始类型就足够了。为此,Java 8 提供了一批避免原始类型装箱的函数式接口。例如IntPredicate、IntConsumer、DoublePredicate、IntFunction等,使用原始类型作为接口命名的前缀便是对应的避免装箱的函数式接口声明。

查看具体声明,读者应该可以发现,这些接口的函数描述符完全没变,只是泛型使用了具体的原始类型来替代,如下:

来源:https://mp.weixin.qq.com/s?__biz=MzIzMzgxOTQ5NA==&mid=2247483845&idx=1&sn=08990fd78e4f62ddf38238660cc4dd64&chksm=e8fe9dccdf8914da580e13a9fc5fee64c135f55d11f3c2de5731f052fa9f1a39060ed6375110&scene=21#wechat_redirect

Java8 BiFunction

Java8内置的函数式编程接口应用场景和方式的更多相关文章

  1. Java8内置的函数式接口

    JDK 1.8 API 包含了很多内置的函数式接口.其中就包括我们在老版本中经常见到的 Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterfac ...

  2. Java8 内置的函数式接口

    1.Java8 内置的四大核心函数式接口 (1)Consumer<T> : 消费型接口         void accept(T t); (2)Supplier<T> : 供 ...

  3. Java8内置的四大核心函数式接口

    package java_8; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import j ...

  4. Java8自定义函数式编程接口和便捷的引用类的构造器及方法

    什么是函数编程接口? 约束:抽象方法有且只有一个,即不能有多个抽象方法,在接口中覆写Object类中的public方法(如equal),不算是函数式接口的方法. 被@FunctionalInterfa ...

  5. Java8新特性:函数式编程

    1. 概述 函数式编程学习目的: 能够看懂公司里的代码 大数据量下处理集合效率更高 代码可读性高 消灭嵌套地狱 函数式编程思想: 面向对象思想需要关注用什么对象完成什么事情.而函数式编程思想就类似于我 ...

  6. [译]Java8:循环与函数式编程

    Java8函数式编程的加入彻底改变了游戏规则.对Java开发者来说这是一个全新的世界,我们也需要做出相应的改变. 在这篇文章中我们将找寻传统循环代码的可替代方案.Java8的函数式编程特性改变了编程思 ...

  7. [译]java8新特性:函数式编程(functional programming)的优点

    Java8引入了函数式编程,他对java是一个极大的扩展.Java从此不在是一个单纯的面向对象语言,现在他同时混合了函数式编程.这是巨大的改变,需要我们调整面对对象的编程习惯,以适应这些变化. 但是为 ...

  8. 20190825 On Java8 第十三章 函数式编程

    第十三章 函数式编程 函数式编程语言操纵代码片段就像操作数据一样容易. 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你 ...

  9. java8 函数式编程接口

    java8 函数式接口java.util.function.* @param T 入参类型 @param R 出参类型 1. Function <T,R> 例: Function<I ...

随机推荐

  1. Python学习:列表、元组、字典、集合

    转载:https://www.cnblogs.com/xc-718/p/9632942.html 列表/元组 列表和元组都是序列结构,它们本身很相似,但又有一点不同: 列表是用方括号标记,如:a=[1 ...

  2. 线程中AutoResetEvent与ManualResetEvent的区别

    线程之间的通信是通过发信号来进行沟通的.. ManualResetEvent发送Set信后后,需要手动Reset恢复初始状态.而对于AutoResetEvent来说,当发送完毕Set信号后,会自动Re ...

  3. Kong(v1.0.2)认证

    介绍 上游服务(api或微服务)的流量通常由各种Kong的authentication plugins的应用程序和配置控制.由于Kong的服务实体表示您自己的上游服务的一对一映射,所以最简单的场景是在 ...

  4. Linux 系统报错 rcu_preempt detected stalls on CPUs/tasks

    说在前面的一些废话: 这是什么错误我不知道,为什么出现我不知道! 那为什么还要把他写出来了,只是因为这个错误遇到了,而且浪费了我很多时间和精力. 故事留给自己看,解决办法就是,重新升级一下Linux系 ...

  5. 软链接ln -s 总结

    ln -s 软链接知识总结 1.软连建立:ln  -s  源文件 软链接文件 2.误区:软链接是创建的,就意味着软链接文件不可以在创建之前存在 3.类比:win快捷方式 4.删除:rm就可以,但源文件 ...

  6. 使用Linux的环境变量

    许多程序和脚本都使用环境变量来获取系统信息,并存储临时数据和配置信息: 1.什么是环境变量 用来存储关于shell会话和工作环境的信息,就叫做环境变量: bash shell下两种类型: 1.全局变量 ...

  7. Linq to SQL -- Insert、Update、Delete

    Insert/Update/Delete操作 插入(Insert) 1.简单形式 说明:new一个对象,使用InsertOnSubmit方法将其加入到对应的集合中,使用SubmitChanges()提 ...

  8. Koa,React和socket.io

    安装  socket.io/socket.io-client 基本用法 首先koa和socket.io代码片段 const server = require('http'). const server ...

  9. PyQt5实现邮件合并功能(GUI)

    1. 实战Word批量 需要处理批量替换word的一些数据,数据源从Excel中来. Excel的百分数会变为数字,以及浮点数会多好多精度,为了原汁原味的数据,直接复制数据到文本文件.通过\t来分隔即 ...

  10. Https证书配置

    本文介绍配置免费证书的方法 具体步骤: 一.获取免费CA证书 步骤1到腾讯云找到: 二.申请完成 后域名验证指引 申请域名型证书,可以通过以下方式验证域名的所有权: 1. 手动 DNS 验证 通过解析 ...