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

@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. 《重构-改善既有代码的设计》学习笔记---Replace Temp with Query(以查询取代临时变量)

    临时变量的问题在于: 它们是暂时的,而且只能在所属函数内使用.由于临时变量只在所属函数内可见,所以,如果很多地方都在用这个临时变量,就会驱使你写出更长的函数.如果把临时变量替换为一个查询,那么其他函数 ...

  2. 去freessl.org申请免费ssl服务器证书

    去freessl.org申请免费ssl服务器证书 来源: 本文链接 来自osnosn的博客 写于: 2019-03-30. 想搞个自签名证书,可以参考这篇: 用openssl为WEB服务器生成证书(自 ...

  3. AngularJS 1.x 思维索引

    我们在这里不讨论Angular2和Angular4,因为其完全重写,其实已经不叫AngularJS了. AngularJS的缺陷: 性能问题:通过检查脏值进行数据更新,当数据不断增加时,检查的效率就不 ...

  4. 视频流PS,PS封装H264

    出处: ISOIEC 13818-1 PS流: PS流由PSGOP组成,每个PSGOP是由I帧起始的多帧集合,每个GOP之间没有相互依赖信息,可以剪切拼接. | PSGOP0 | PSGOP1 | P ...

  5. 2018-2019-2 20175311 实验二 《Java开发环境的熟悉》实验报告

    2018-2019-2 20175303 实验二 <Java开发环境的熟悉>实验报告 一.实验准备 1.了解掌握实验所要用到的三种代码 伪代码 产品代码 测试代码 2.IDEA中配置单元测 ...

  6. iOS设置截图背景图片透明

    /** 设置图片背景为透明 */- (UIImage *)imageToTransparent { // 分配内存 const int imageWidth = self.size.width; co ...

  7. sas 9.4 sid 64bit 到期时间210804 带有EM

    PROC SETINIT RELEASE='9.4';SITEINFO NAME='NATIONAL PINGTUNG UNI OF SCIENCE&TECH'SITE=12001462 OS ...

  8. xpath知多少

    XPath 语法 XPath 使用路径表达式来选取 XML 文档中的节点或节点集.节点是通过沿着路径 (path) 或者步 (steps) 来选取的. XML 实例文档 我们将在下面的例子中使用这个 ...

  9. java.lang.Byte 类源码浅析

    Byte 类字节,属于Number. public final class Byte extends Number implements Comparable<Byte> { /** * ...

  10. Pandas学习笔记(二)

    (1)Pandas处理以下三个数据结构 系列(Series) 数据帧(DataFrame) 面板(Panel) 这些数据结构构建在Numpy数组之上,这意味着它们很快.考虑这些数据结构的最好方法是,较 ...