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 ...
随机推荐
- [ZZ] matlab中小波变换函数dwt2和wavedec2 系数提取函数appcoef2和detcoef2
https://zhidao.baidu.com/question/88038464.html DWT2是二维单尺度小波变换,其可以通过指定小波或者分解滤波器进行二维单尺度小波分解. 而WAVEDEC ...
- Git在已有的分支上新建个人分支开发
在Dev分支上新建一个分支(可以通过Git TE网页创建) 然后就可以从Source下拉列表中看到新建的分支(new_name1)了. 远程分支创建完成之后,就可以在本机上面使用Git GUI Her ...
- SSO单点登录三种情况的实现方式详解
单点登录(SSO——Single Sign On)对于我们来说已经不陌生了.对于大型系统来说使用单点登录可以减少用户很多的麻烦.就拿百度来说吧,百度下面有很多的子系统——百度经验.百度知道.百度文库等 ...
- Centos 7环境下安装配置Hadoop 3.0 Beta1简记
前言 由于以前已经写过一篇Centos 7环境下安装配置2.8的随笔,因此这篇写得精简些,只挑选一些重要环节记录一下. 安装环境为:两台主机均为Centos 7.*操作系统,两台机器配置分别为: 主机 ...
- SAS 统计某个数据集各个字段频数,并汇集到一个表中
/*统计表的字段*/ PROC CONTENTS DATA=SASHELP.CLASS NOPRINT OUT=CA(KEEP=NAME); RUN; /*提取表的变量名*/ PROC SQL NOP ...
- ngnix和负载均衡
1 准备环境 =====>part1: iptables -F #systemctl disable firewalld #开机默认关闭 #systemctl stop firewalld #立 ...
- spring事务学习笔记
事务管理器 default使用数据库默认的隔离级别
- PHP语言入门的学习方法十要素
对于PHP程序设计语言来说.每个人的学习方式不同,写这篇文章的目的是分享一下自己的学习过程,仅供参考,不要一味的用别人的学习方法,找对自己有用的学习方式.经常在某些论坛和QQ群里看到一些朋友会问“怎样 ...
- 【HQL】函数汇总
背景 抽空整理一篇HQL函数及常用的小技巧 COALESCE COALESCE(T v1, T v2, -) 返回参数中的第一个非空值:如果所有值都为NULL,那么返回NULL
- Exception in thread "main" java.lang.UnsupportedClassVersionError
Exception in thread “main" java.lang.UnsupportedClassVersionError这个报错是用高版本JDK编译项目或文件产生的class文件, ...