一. 函数式编程

Java8所有的新特性基本基于函数式编程的思想,函数式编程的带来,给Java注入了新鲜的活力。

下面来近距离观察一下函数式编程的几个特点:

  • 函数可以作为变量、参数、返回值和数据类型。
  • 基于表达式来替代方法的调用
  • 函数无状态,可以并发和独立使用
  • 函数无副作用,不会修改外部的变量
  • 函数结果确定性;同样的输入,必然会有同样的结果。

下面jdk1.8里面对函数式编程的定义。只是一个  FunctionalInterface 接口特别的简单。

 @Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

这个函数式接口有几点以下的限制:

  • 唯一的抽象方法,有且仅有一个 (即所有的函数式接口,有且只能有一个抽象方法)
  • 加上标注,则会触发JavaCompiler的检查。对于符合函数接口的接口,加不加都无关紧要,但是加上则会提供一层编译检查的保障。如果不符合,则会报错。
  • 不能被覆盖之后,再声明为抽象方法,则不算抽象方法。例如接口实现了Object中的方法。 
  • 可用于lambda类型的使用方式

二. Java8新增函数式接口

Stream的操作是建立在函数式接口的组合之上的。Java8中新增的函数式接口都在java.util.function包下。这些函数式接口可以有多种分类方式。

2.1 Function

Function是从T到R的一元映射函数。将参数T传递给一个函数,返回R。即R = Function(T)

Function最常用的应该是  <R> Stream<R> map(Function<? super T, ? extends R> mapper);

比如List<Person> person里面有age,name.... 我传入age,他就会返回age的集合给我。

 @FunctionalInterface
public interface Function<T, R> { R apply(T t);

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
} default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
} static <T> Function<T, T> identity() {
return t -> t;
}
}

2.2 Predicate

Predicate是一个谓词函数,主要作为一个谓词演算推导真假值存在,返回布尔值的函数。Predicate等价于一个Function的boolean型返回值的子集。

predicate最常用的莫过于  Stream<T> filter(Predicate<? super T> predicate);  

比如我要过滤年龄 > 18 的人,我传入age,判断是否为true。为true则保留,false丢弃。

 @FunctionalInterface
public interface Predicate<T> { boolean test(T t);

default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
} default Predicate<T> negate() {
return (t) -> !test(t);
} default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}

2.3 Consumer

Consumer是从T到void的一元函数,接受一个入参但不返回任何结果的操作。

Consumer最常用的肯定是   default void forEach(Consumer<? super T> action) {}

这是一段forEach循环的代码,传入实现的方法,并不返回任何值。只是循环。

 @FunctionalInterface
public interface Consumer<T> { void accept(T t);

default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

三. Lambda表达式

3.1 基本语法

Lambda 的基本结构为 (arguments) -> body,有如下几种情况:

  • 参数类型可推导时,不需要指定类型,如 (a) -> System.out.println(a)
  • 当只有一个参数且类型可推导时,不强制写 (), 如 a -> System.out.println(a)
  • 参数指定类型时,必须有括号,如 (int a) -> System.out.println(a)
  • 参数可以为空,如 () -> System.out.println(“hello”)
  • body 需要用 {} 包含语句,当只有一条语句时 {} 可省略

3.2 Lambda原理

比如如下代码:

 List<Integer> list = new ArrayList<>();

 list.stream().filter((x) -> x >= 18)

 Stream<T> filter(Predicate<? super T> predicate);

 @FunctionalInterface
public interface Predicate<T> { boolean test(T t); }

比如List里面存个个人的年龄,现在筛选出年龄大于等于18的人。

此时我们就可以用  list.stream().filter((x) -> x >= 18)   这就是一个典型的lambda表达式

(x) -> x >= 18 传给  Predicate 函数式接口。

原理其实是:

JVM帮我们动态生成了一个内部类,然后这个内部类实现了 Predicate 这个函数式接口。

重写了里面的test方法。生成的类似如下:

 static final class Main$$Lambda$1 implements Predicate<Integer> {
private Main$$Lambda$1() {
} @Override
public boolean test(Integer x) {
return x >= 18;
}
}

3.3 Lambda用法

 public class Main {
public static void main(String[] args) { List<Integer> list = new ArrayList<>();
list.add(40);
list.add(50);
list.add(20);
list.add(30);
List<Integer> collect = list.stream().filter(x -> x >= 30)
.map((x) -> x + 10).sorted((x, y) -> -x.compareTo(y))
.collect(Collectors.toList());
System.out.println(collect);
}
}

这个一段很典型的Lambda + Stream的用法。

  • list.stream()获取list的stream的流
  • filter筛选出年龄大于30的人 (里面是一个Predicate接口,返回真假)
  • map做一个function映射
  • sort排序,里面是compartor

四. 总结

Lambda 表达式可以减少很多代码,能提高生产力。但也要理解其原理。比如3.3中的代码,为什么filter里面是断言表达式,map里面是function表达式。

这都要从lambda的原理入手,也就是JVM动态生成一个内部类,并继承其中的抽象方法。

本次主要介绍了Java函数式编程的原理以及应用,主要从Stream和lambda入手。通过一些简单的概念,以及代码,更好的理解Java的函数式编程。

掌握Java的函数式编程,对平时我们开发代码,看其他人的代码,都有很大的帮助。

且行且珍惜,加油!

Java函数式编程原理以及应用的更多相关文章

  1. 《深入理解Java函数式编程》系列文章

    Introduction 本系列文将帮助你理解Java函数式编程的用法.原理. 本文受启发于JavaOne 2016关于Lambda表达式的相关主题演讲Lambdas and Functional P ...

  2. paip.提升效率---filter map reduce 的java 函数式编程实现

    #paip.提升效率---filter map reduce 的java 函数式编程实现 ======================================================= ...

  3. Java并发编程原理与实战五:创建线程的多种方式

    一.继承Thread类 public class Demo1 extends Thread { public Demo1(String name) { super(name); } @Override ...

  4. Java函数式编程:一、函数式接口,lambda表达式和方法引用

    Java函数式编程 什么是函数式编程 通过整合现有代码来产生新的功能,而不是从零开始编写所有内容,由此我们会得到更加可靠的代码,并获得更高的效率 我们可以这样理解:面向对象编程抽象数据,函数式编程抽象 ...

  5. Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化

    承接上文:Java函数式编程:一.函数式接口,lambda表达式和方法引用 这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程. 本篇博客主要聊聊以 ...

  6. Java函数式编程:三、流与函数式编程

    本文是Java函数式编程的最后一篇,承接上文: Java函数式编程:一.函数式接口,lambda表达式和方法引用 Java函数式编程:二.高阶函数,闭包,函数组合以及柯里化 前面都是概念和铺垫,主要讲 ...

  7. Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...

  8. Java 函数式编程(Lambda表达式)与Stream API

    1 函数式编程 函数式编程(Functional Programming)是编程范式的一种.最常见的编程范式是命令式编程(Impera Programming),比如面向过程.面向对象编程都属于命令式 ...

  9. 精练代码:一次Java函数式编程的重构之旅

    摘要:通过一次并发处理数据集的Java代码重构之旅,展示函数式编程如何使得代码更加精练. 难度:中级 基础知识 在开始之前,了解"高阶函数"和"泛型"这两个概念 ...

随机推荐

  1. [剑指offer] 46. 孩子们的游戏(圆圈中最后剩下的数)

    题目描述 随机指定一个数m,让编号为0的小朋友开始报数.每次喊到m-1的那个小朋友要出列,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友 ...

  2. Redis(二)--- Redis的底层数据结构

    1.Redis的数据结构 Redis 的底层数据结构包含简单的动态字符串(SDS).链表.字典.压缩列表.整数集合等等:五大数据类型(数据对象)都是由一种或几种数结构构成. 在命令行中可以使用 OBJ ...

  3. python注释-输入输出-基本数据类型-运算符

    python注释 用处:注释用来书写一些解释性信息,对代码的逻辑作用等作出描述 单行注释.多行注释 # 这是行注释,注释内容与# 之间要空一格 print("hello world!&quo ...

  4. maven install时跳过测试

    xl_echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! - ...

  5. [03] HEVD 内核漏洞之UAF

    作者:huity出处:https://www.cnblogs.com/huity35/p/11240997.html版权:本文版权归作者所有.文章在博客园.个人博客同时发布.转载:欢迎转载,但未经作者 ...

  6. 十九、表添加字段的SQL语句写法

    通用式: alter table [表名] add [字段名] 字段属性 default 缺省值 default 是可选参数增加字段: alter table [表名] add 字段名 smallin ...

  7. JavaScript基础学习第六天

    目标: 能够使用对象的方式处理数据 ☞ 代码预解析: 1. 变量提升 :当程序中遇到定义变量后,就会将该变量的定义提升到当前作用域的开始位置,不包括变量的赋值 2. 函数提升:当程序中遇到函数的声明时 ...

  8. django第四次(转自刘江)

    我们都知道对于ManyToMany字段,Django采用的是第三张中间表的方式.通过这第三张表,来关联ManyToMany的双方.下面我们根据一个具体的例子,详细解说中间表的使用. 一.默认中间表 首 ...

  9. 多线程编程(Linux C)

    多线程编程可以说每个程序员的基本功,同时也是开发中的难点之一,本文以Linux C为例,讲述了线程的创建及常用的几种线程同步的方式,最后对多线程编程进行了总结与思考并给出代码示例. 一.创建线程 多线 ...

  10. spark shuffle写操作三部曲之UnsafeShuffleWriter

    前言 在前两篇文章 spark shuffle的写操作之准备工作 中引出了spark shuffle的三种实现,spark shuffle写操作三部曲之BypassMergeSortShuffleWr ...