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

@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. Python2.X和Python3.X的w7同时安装使用

    一.介绍 Python([ˈpaɪθən])是一种面向对象.解释型计算机程序设计语言.Python语法简洁.清晰,具有丰富和强大的类库. Python源代码遵循GPL(GNU General Publ ...

  2. 嵌入式全志V3s荔枝派板卡移植 aircrack-ng

    我使用的是aircrack-ng-1.1.tar.gz这个版本的包. 编辑common.mak,在27行COMMON_FLAGS = 下加入嵌入式开发的环境 -I 和 -L : COMMON_CFLA ...

  3. Windows硬链接 软链接 符号链接 快捷方式

    http://blog.nsfocus.net/shortcuthard-linkjunction-pointsymbolic-link/ Windows支持4种”链接”机制,分别是shortcut. ...

  4. MySql 的SQL执行计划查看,判断是否走索引

    在select窗口中,执行以下语句: set profiling =1; -- 打开profile分析工具show variables like '%profil%'; -- 查看是否生效show p ...

  5. 从9x9矩阵中抽取中间菱形区域打印 - perl

    起因: 源自于c的练习题,打印diamond,因为perl不需编译,方便调试,故先用perl实现一下 关键: 没有想到好的思路,只能借助于上一篇打印上下三角矩阵的方法,把菱形拆成上下左右4个三角矩阵区 ...

  6. 编译问题:'<invalid-global-code>' does not contain a definition for 'Store' and no extension method 'XXX' accepting a first argument of type '<invalid-global-code>' could be found

    这是VS2015上的bug. 我碰到的时候,是VS在合并两个分支的代码时,多加了一个}.导致编译语法报错.. 解决办法就是在错误的附近,找找有没有多余的大括号,删掉即可. 这个问题在vs2017上面没 ...

  7. SAS-决策树模型

    决策树是日常建模中使用最普遍的模型之一,在SAS中,除了可以通过EM模块建立决策树模型外,还可以通过SAS代码实现.决策树模型在SAS系统中对应的过程为Proc split或Proc hpsplit, ...

  8. MSF banner

    ____________ [%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%| $a, |%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%] [%%%%%%%%%%%%%% ...

  9. MemCache在网站中的使用

    MemCache安装好后,网站一直没法使用,后来查找资料,发现需要在配置文件里写几行代码,如下所示 <enyim.com> <memcached protocol="Tex ...

  10. C#中替换特殊字符串

    private string change字符(string str) { if (!string.IsNullOrEmpty(str)) { //将特殊字符全部替换为下划线 string patte ...