从java8 说起函数式编程
写在前面
为什么要用函数式编程。看例子:
final List<BigDecimal> prices = Arrays.asList(
new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"),
new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"),
new BigDecimal("45"), new BigDecimal("12"));
BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO;
for(BigDecimal price : prices) {
if(price.compareTo(BigDecimal.valueOf(20)) > 0)
totalOfDiscountedPrices = totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9)));
}
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
更优雅的方式,是使用声明式的代码:
final BigDecimal totalOfDiscountedPrices =
prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
使用函数式代码的好处:
- 减少了可变量(Immutable Variable)的声明
- 能够更好的利用并行(Parallelism)
- 代码更加简洁和可读
函数式接口
函数式接口就是仅声明了一个方法的接口,比如我们熟悉的Runnable,Callable,Comparable等都可以作为函数式接口。当然,在Java 8中,新添加了一类函数式接口,如Function,Predicate,Consumer,Supplier等。
@FunctionalInterface
public static interface IntHandler{
void handle(int i);
}
在函数式接口中,可以声明0个或者多个default方法,这些方法在接口内就已经被实现了。因此,接口的default方法也是Java 8中引入的一个新概念。
default method 默认方法,又称作虚拟扩展方法或保卫者方法,例子:
import java.util.Objects;
import java.util.function.Consumer;
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
为什么需要default method?
- 即使你的API已经发布出去了,你依然可以为接口添加新方法并且无需考虑向后兼容问题。
- java8 对Lambda的支持必然会影响JDK API的接口,如果直接在接口中添加方法,就会导致所有实现该接口的类或者接口无法通过编译。
函数式接口使用 @FunctionalInterface
注解进行标注。虽然这个注解的使用不是强制性的,但是使用它的好处是让此接口的目的更加明确,同时编译器也会对代码进行检查,来确保被该注解标注的接口的使用没有语法错误。
lambda表达式
lambda表达式即匿名函数,它是一段没有函数名的函数体,可以作为参数直接传递给相关调用者。
Lambda 表达式通常使用 (argument) -> (body) 语法书写,例如:
(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
举例来说:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
Lambda 表达式的结构
- 一个 Lambda 表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
- 空圆括号代表参数集为空。例如:() -> 42
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
- Lambda 表达式的主体可包含零条或多条语句
- 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
- 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用。
Runnable r = () -> System.out.println("hello world");
方法引用
方法引用是Java 8中提出的用来简化lambda表达式的一种手段。它通过类名和方法名来定位到一个静态方法或者实例方法。 方法引用在Java 8中的使用非常灵活。总的来说,可以分为以下几种:
静态方法引用:ClassName::methodName
List<Integer> ints = Arrays.asList(1, 2, 3);
ints.sort(Integer::compare);
实例上的实例方法引用 instanceReference::methodName,方法引用使用”::”定义,”::”的前半部分表示类名或者实例名,后半部分表示方法名称。如果是构造函数,则使用new表示。
words.forEach(System.out::println); words.stream().map(word -> word.length()); // lambda
words.stream().map(String::length); // method reference // lambda
words.stream().map(word -> {
return new StringBuilder(word);
});
// constructor reference
words.stream().map(StringBuilder::new);
Lambda 表达式举例
1.线程初始化
//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from thread");
}
}).start();
//新方法:
new Thread(
() -> System.out.println("Hello from thread")
).start();
2.打印数组所有元素,在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:
//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}
//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);
3.断言函数式接口使用
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.println("Print all numbers:");
evaluate(list, n -> true);
System.out.println("Print no numbers:");
evaluate(list, n -> false);
System.out.println("Print even numbers:");
evaluate(list, n -> n % 2 == 0);
System.out.println("Print odd numbers:");
evaluate(list, n -> n % 2 == 1);
System.out.println("Print numbers greater than 5:");
evaluate(list, n -> n > 5);
}
public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list) {
if (predicate.test(n))
System.out.print(n + " ");
}
}
}
Lambda 表达式与匿名类的区别
使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。
Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法
接口与抽象类
虽然 Java 8 的接口的默认方法就像抽象类,能提供方法的实现,但是他们俩仍然是 不可相互代替的:
- 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
- 接口中没有 this 指针,没有构造函数,不能拥有实例字段(实例变量)或实例方法,无法保存 状态(state),抽象方法中可以。
- 抽象类不能在 java 8 的 lambda 表达式中使用。
- 从设计理念上,接口反映的是 “like-a” 关系,抽象类反映的是 “is-a” 关系。
从java8 说起函数式编程的更多相关文章
- Java8新特性--函数式编程
在jdk8中什么是函数式接口: 1.被@FunctionalInterface注解修饰的. 2.接口里边只有一个非default的方法. 满足以上2个条件的即为函数式接口,ps:即使一个接口没有被@F ...
- Java8 新特性 —— 函数式编程
本文部分摘录自 On Java 8 概述 通常,传递给方法的数据不同,结果也不同.同样的,如果我们希望方法被调用时的行为不同,该怎么做呢?结论是:只要能将代码传递给方法,那么就可以控制方法的行为. 说 ...
- java8 array、list操作 汇【4】)- Java8 Lambda表达式 函数式编程
int tmp1 = 1; //包围类的成员变量 static int tmp2 = 2; //包围类的静态成员变量 //https://blog.csdn.net/chengwangbaiko/ar ...
- [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念
本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程? java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...
- [译]java8新特性:函数式编程(functional programming)的优点
Java8引入了函数式编程,他对java是一个极大的扩展.Java从此不在是一个单纯的面向对象语言,现在他同时混合了函数式编程.这是巨大的改变,需要我们调整面对对象的编程习惯,以适应这些变化. 但是为 ...
- 重识Java8函数式编程
前言 最近真的是太忙忙忙忙忙了,很久没有更新文章了.最近工作中看到了几段关于函数式编程的代码,但是有点费解,于是就准备总结一下函数式编程.很多东西很简单,但是如果不总结,可能会被它的各种变体所困扰.接 ...
- JavaScript ES6函数式编程(一):闭包与高阶函数
函数式编程的历史 函数的第一原则是要小,第二原则则是要更小 -- ROBERT C. MARTIN 解释一下上面那句话,就是我们常说的一个函数只做一件事,比如:将字符串首字母和尾字母都改成大写,我们此 ...
- 【JS】394- 简明 JavaScript 函数式编程-入门篇
转载自公众号"程序员成长指北" 写在开头 本文较长,总共分为三大部分:(对于函数式编程以及其优点有一定理解的童鞋,可以直接从 第二部分 开始阅读) 第一部分:首先会通过实际代码介绍 ...
- 函数式编程 -> Lambda
一.函数式编程 函数式编程,同面向对象编程.指令式编程一样,是一种软件编程范式,在多种编程语言中都有应用.百科词条中有很学术化的解释,但理解起来并不容易.不过,我们可以借助于数学中函数的概念,来理解函 ...
随机推荐
- python使用一个集合代替列表
"""说明:对于一个指定的序列,如果需要获得一个只包含该序列中不重复的序列时,使用以下算法:"""seq=['a','a','b','c', ...
- JAVA学习第三十课(经常使用对象API)- String类:类方法练习
intern方法 public class Main { public static void main(String[] args) { String str1 = new String(" ...
- javascript正則表達式 "\b"问题
preface 昨晚在看<javascript权威指南>后.看见作者自己封装一个兼容全部浏览器的山寨HTML5新API classLIst类.自己想了想认为自己也要去玩一下.可是能力还是有 ...
- 未知宽高div水平垂直居中3种方法
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head&g ...
- 基于Hadoop的大数据平台实施记——整体架构设计[转]
http://blog.csdn.net/jacktan/article/details/9200979 大数据的热度在持续的升温,继云计算之后大数据成为又一大众所追捧的新星.我们暂不去讨论大数据到底 ...
- js将日期格式的时候转换成时间搓
自己写的一个方法 function split_time(time){//将当前时间转换成时间搓 例如2013-09-11 12:12:12 var arr=time.split(" ...
- AIX 常用命令和知识
BOOTLIST:#bootlist -m normal -o (查看bootlist)#bootlist -m normal (设置bootlist为空,谁要在我机器上执行我就要哭了)#boot ...
- CSS基础知识笔记(二)之选择器
CSS选择器 选择器{ 样式; } 每一条css样式声明(定义)由两部分组成,形式如下: 在{}之前的部分就是“选择器”,“选择器”指明了{}中的“样式”的作用对象,也就是“样式”作用于网页中的哪些元 ...
- 1 Intellij IDEA 个人常用快捷方式
个人常用快捷方式 alt+enter 万能键 inn if(xxx!=null) iter foreach ct ...
- 关于Ajax的技术组成与核心原理
1.Ajax 特点: 局部刷新.提高用户的体验度,数据从服务器商加载 2.AJax的技术组成 不是新技术,而是之前技术的整合 Ajax: Asynchronous Javascript And Xml ...