Java8内置的函数式编程接口应用场景和方式
首先,我们先定义一个函数式编程接口
@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等,使用原始类型作为接口命名的前缀便是对应的避免装箱的函数式接口声明。
查看具体声明,读者应该可以发现,这些接口的函数描述符完全没变,只是泛型使用了具体的原始类型来替代,如下:

Java8内置的函数式编程接口应用场景和方式的更多相关文章
- Java8内置的函数式接口
JDK 1.8 API 包含了很多内置的函数式接口.其中就包括我们在老版本中经常见到的 Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterfac ...
- Java8 内置的函数式接口
1.Java8 内置的四大核心函数式接口 (1)Consumer<T> : 消费型接口 void accept(T t); (2)Supplier<T> : 供 ...
- Java8内置的四大核心函数式接口
package java_8; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import j ...
- Java8自定义函数式编程接口和便捷的引用类的构造器及方法
什么是函数编程接口? 约束:抽象方法有且只有一个,即不能有多个抽象方法,在接口中覆写Object类中的public方法(如equal),不算是函数式接口的方法. 被@FunctionalInterfa ...
- Java8新特性:函数式编程
1. 概述 函数式编程学习目的: 能够看懂公司里的代码 大数据量下处理集合效率更高 代码可读性高 消灭嵌套地狱 函数式编程思想: 面向对象思想需要关注用什么对象完成什么事情.而函数式编程思想就类似于我 ...
- [译]Java8:循环与函数式编程
Java8函数式编程的加入彻底改变了游戏规则.对Java开发者来说这是一个全新的世界,我们也需要做出相应的改变. 在这篇文章中我们将找寻传统循环代码的可替代方案.Java8的函数式编程特性改变了编程思 ...
- [译]java8新特性:函数式编程(functional programming)的优点
Java8引入了函数式编程,他对java是一个极大的扩展.Java从此不在是一个单纯的面向对象语言,现在他同时混合了函数式编程.这是巨大的改变,需要我们调整面对对象的编程习惯,以适应这些变化. 但是为 ...
- 20190825 On Java8 第十三章 函数式编程
第十三章 函数式编程 函数式编程语言操纵代码片段就像操作数据一样容易. 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你 ...
- java8 函数式编程接口
java8 函数式接口java.util.function.* @param T 入参类型 @param R 出参类型 1. Function <T,R> 例: Function<I ...
随机推荐
- iBatis.Net的基本情况和运行原理
转载http://www.cnblogs.com/13590/archive/2013/02/27/2934580.html 摘要:介绍iBatis.Net的基本情况和运行原理,运行环境中各参数的配置 ...
- 关于HTML和CSS一些鸡零狗碎的事
原文发表于我自己的服务器www.jjxiaoliu.cn:不过这个服务器我可能不打算续费了,所以搬到cnblogs来. 有些关于HTML和CSS的内容不值得单独列一篇文章,全都放在这里了. 我们可以利 ...
- 二叉树遍历(flist)(二叉树,已知中序层序,求先序)
问题 C: 二叉树遍历(flist) 时间限制: 1 Sec 内存限制: 128 MB提交: 76 解决: 53[提交][状态][讨论版][命题人:quanxing][Edit] [TestDat ...
- ES6系列之箭头函数
本系列是在平时阅读.学习.实际项目中有关于es6中的新特性.用发的简单总结,目的是记录以备日后温习:本系列预计包含let/const.箭头函数.解构.常用新增方法.Symbol.Set&Map ...
- Unity 代码组件获取和使用、Resources加载、OnGUI、Time、Mathf、PlayerPref
1. 游戏物体组件获取.添加组件(重要) 作业分析: 子弹生成:坦克生成----->坦克控制类里生成子弹 子弹飞行:子弹自己飞,不能通过坦克控制类进行管理: 获取代码组件,设置子弹速度: ...
- MySQL查询语句报错 sql_mode=only_full_group_by 问题
升级MySQL到5.7后,查询语句总是报sql_mode=only_full_group_by问题,总结归纳了两种解决方案,推存第二种解决方案. 报错信息: [Err] 1055 - Expressi ...
- 利用CNN神经网络实现手写数字mnist分类
题目: 1)In the first step, apply the Convolution Neural Network method to perform the training on one ...
- Web 应用程序项目 Himall.Web 已配置为使用 IIS。 无法访问 IIS 元数据库
Web应用程序项目XXXX已配置为使用IIS.无法访问IIS元数据库.您没有足够的特权访问计算机上的IIS网站,xxxxiis 问题:Windows8下直接使用VS打开项目,出现问题:XXXX已配置为 ...
- Jquery判断$("#id")获取的对象是否存在
一.判断对象对象是否存在 如果是下面的 jQuery 代码判断一个对象是否存在,是不能用的 代码如下: if($("#id")){ }else{} 因为 $(“#id”) 不管 ...
- N个工作日后的日期
这里对特殊日期采用了模拟的方式,在实际开发中当然这些数据是从数据库中读取,调用方法时 只需传入开始时间(一般当前) 和N(代表N个工作日) /// <summary> /// 获取时间 / ...