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

@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. HTML与CSS:结构与表现

    在HTML和CSS里存在着部分重复的功能,例如两者都可以设定一段文字的字体属性.既然如此,为啥还要CSS呢(至少,为啥CSS里存在着和HTML标签属性重复的东西呢)? 这是因为,HTML和CSS的用途 ...

  2. 工控随笔_06_西门子_Step7归档项目无法备份的解决方法

    在一次备份Step7项目时,突然发现无法进行备份而是报错,具体的报错内容如下所示: 图 step7 归档程序时报pkzipc.exe 应用程序错误  内存不能为"read" 一.s ...

  3. H264--2--语法及结构

    转自:http://blog.csdn.net/yangzhongxuan/article/details/8003494 名词解释 场和帧 :    视频的一场或一帧可用来产生一个编码图像.在电视中 ...

  4. nginx1.14.0版本https加密配置

    修改host文件,为最后访问域名准备 C:\Windows\System32\drivers\etc host文件目录192.168.10.140 www.joyce.com 在最后添加这个自定义域名 ...

  5. ubuntu 装机步骤表

    步骤 1. root 步骤 apt-get update ; apt-get upgrate apt-get install git zsh apt-get install -y make build ...

  6. socket开发总结

    1.connect 阻塞socket connect时会等待返回结果,等于0表示成功,小于0表示失败. 非阻塞socket connect时会立刻返回结果,等于0表示成功,小于0且errno == E ...

  7. ES6 实战项目构建 ES6+glup+express

    ES6推出已经有几个年头了,平时也有学过一些基本语法,无奈实践经验太少.而且前端早已脱离了刀耕火种的时代,一些自动化构建工具像gulp.webpack等也需要熟练掌握.最近刚签了三方,闲暇之余就找了个 ...

  8. C语言数据结构基础学习笔记——B树

    2-3树:是一种多路查找树,包含2结点和3结点两种结点,其所有叶子结点都在同一层次. 2结点:包含一个关键字和两个孩子(或没有孩子),其左孩子的值小于该结点,右孩子的值大于该结点. 3结点:包含两个关 ...

  9. C语言数据结构基础学习笔记——图

    图(G)由顶点集(V)和边集(E)组成,G=(V,E) 常用概念: ①V(G)表示图G中顶点的有限非空集,V永不为空: ②用|V|表示图G中顶点的个数,也称为图G的阶: ③E(G)表示图G中顶点之间关 ...

  10. class中限定绑定属性__slots__方法

    使用__slots__但是,如果我们想要限制class的属性怎么办?比如,只允许对Student实例添加name和age属性.为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的 ...