3分钟看完Java 8——史上最强Java 8新特性总结之第一篇 函数式编程基础
目录
· 行为参数化
· 概况
· 函数式接口
· 类型推断
· 使用外层变量
· 方法引用
行为参数化
1. 理解函数式编程要先理解行为参数化。
2. 行为参数化:一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
3. 行为参数化优点:可让代码更好地适应不断变化的需求,减轻未来的工作量。
4. 实现方式
a) Java 8以前:通过接口实现类或接口匿名类实现。
b) Java 8及以后:通过Lambda表达式实现。
5. 举例
a) 通过接口实现类实现行为参数化
i. Apple.java(后续举例将多次使用到该类)
public class Apple { private Integer weight; private String color; public Apple(Integer weight, String color) {
this.weight = weight;
this.color = color;
} public Integer getWeight() {
return weight;
} public void setWeight(Integer weight) {
this.weight = weight;
} public String getColor() {
return color;
} public void setColor(String color) {
this.color = color;
} @Override
public String toString() {
return "weight=" + weight + " color=" + color;
}
}
ii. ApplePredicate.java
public interface ApplePredicate { boolean test(Apple apple); }
iii. AppleHeavyWeightPredicate.java
public class AppleHeavyWeightPredicate implements ApplePredicate { public boolean test(Apple apple) {
return apple.getWeight() > 150;
} }
iv. AppleGreenColorPredicate.java
public class AppleGreenColorPredicate implements ApplePredicate { public boolean test(Apple apple) {
return "green".equals(apple.getColor());
} }
v. Test.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; public class Test { public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
} public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
List<Apple> newInventory1 = filterApples(inventory, new AppleHeavyWeightPredicate());
printApples(newInventory1);
System.out.println("-----");
List<Apple> newInventory2 = filterApples(inventory, new AppleGreenColorPredicate());
printApples(newInventory2);
} }
b) 通过接口匿名类实现行为参数化
i. ApplePredicate.java
public interface ApplePredicate { boolean test(Apple apple); }
ii. Test.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; public class Test { public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
} public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
List<Apple> newInventory1 = filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
});
printApples(newInventory1);
System.out.println("-----");
List<Apple> newInventory2 = filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
});
printApples(newInventory2);
} }
Lambda表达式
概况
1. Lambda表达式:可把Lambda表达式看作只有一个方法的接口匿名类,即没有声明名称的方法,也可以作为参数传递给另一个方法。
2. Lambda表达式特点
a) 匿名:不像普通的方法那样有一个明确的名称。
b) 函数:不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
c) 传递:可以作为参数传递给方法或存储在变量中。
d) 简洁:无需像匿名类那样写很多模板代码。
3. Lambda表达式语法
a) 语法格式1
(parameters) -> expression
b) 语法格式2
(parameters) -> { statements; }
c) 举例
场景 |
Lambda表达式 |
布尔表达式 |
(List<String> list) -> list.isEmpty() |
创建对象 |
() -> new Apple(10, "red") |
消费一个对象 |
(Apple a) -> { System.out.println(a.getWeight()); } |
从一个对象中选择/抽取 |
(String s) -> s.length() |
组合两个值 |
(int a, int b) -> a * b |
比较两个对象 |
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) |
4.Lambda表达式使用条件:只能在函数式接口上使用。
5.函数式接口(Functional Interface):只定义一个抽象方法的接口(注意不包括默认方法)。
6.举例:通过Lambda表达式实现行为参数化
a) ApplePredicate.java
public interface ApplePredicate { boolean test(Apple apple); }
b) Test.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; public class Test { public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
} public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
List<Apple> newInventory1 = filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);
printApples(newInventory1);
System.out.println("-----");
List<Apple> newInventory2 = filterApples(inventory, (apple) -> "green".equals(apple.getColor()));
printApples(newInventory2);
} }
函数式接口
1. Java 8自带的函数式接口都在java.util.function包下。
2. 异常:Java 8自带函数式接口都不允许抛出Checked Exception。如果需要Lambda表达式来抛出异常,要么定义一个自己的函数式接口,并声明Checked Exception,要么把Lambda包在一个try/catch块中。
3. 装箱操作(Boxing):为了避免装箱操作带来的开销问题,不应使用Predicate<T>或Function<T, R>等通用函数式接口,而应使用IntPredicate、IntToLongFunction等原始类型特化接口。
4. Java 8常用函数式接口
函数式接口 |
函数描述符 |
原始类型特化 |
Predicate<T> |
T->boolean |
IntPredicate LongPredicate DoublePredicate |
Consumer<T> |
T->void |
IntConsumer LongConsumer DoubleConsumer |
Function<T,R> |
T->R |
IntFunction<R> IntToDoubleFunction IntToLongFunction LongFunction<R> LongToDoubleFunction LongToIntFunction DoubleFunction<R> ToIntFunction<T> ToDoubleFunction<T> ToLongFunction<T> |
Supplier<T> |
()->T |
BooleanSupplier IntSupplier LongSupplier DoubleSupplier |
UnaryOperator<T> |
T->T |
IntUnaryOperator LongUnaryOperator DoubleUnaryOperator |
BinaryOperator<T> |
(T,T)->T |
IntBinaryOperator LongBinaryOperator DoubleBinaryOperator |
BiPredicate<L, R> |
(L, R) -> boolean |
|
BiConsumer<T, U> |
(T, U) -> void |
ObjIntConsumer<T> ObjLongConsumer<T> ObjDoubleConsumer<T> |
BiFunction<T, U, R> |
(T, U) -> R |
ToIntBiFunction<T, U> ToLongBiFunction<T, U> ToDoubleBiFunction<T, U> |
5. 举例
a) Lambda表达式与函数式接口对应
场景 |
Lambda表达式 |
对应的函数式接口 |
布尔表达式 |
(List<String> list) -> list.isEmpty() |
Predicate<List<String>> |
创建对象 |
() -> new Apple(10, "red") |
Supplier<Apple> |
消费一个对象 |
(Apple a) -> { System.out.println(a.getWeight()); } |
Consumer<Apple> |
从一个对象中选择/抽取 |
(String s) -> s.length() |
Function<String, Integer>或 ToIntFunction<String> |
组合两个值 |
(int a, int b) -> a * b |
IntBinaryOperator |
比较两个值 |
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) |
Comparator<Apple> 或 BiFunction<Apple, Apple, Integer> 或 ToIntBiFunction<Apple, Apple> |
b) Predicate<T>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate; public class Test { public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T s : list) {
if (p.test(s)) {
results.add(s);
}
}
return results;
} public static void main(String[] args) {
List<String> listOfStrings = Arrays.asList("", "A", "B", "", "C");
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
for (String string : nonEmpty) {
System.out.println(string);
}
} }
c) Consumer<T>
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer; public class Test { public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i: list){
c.accept(i);
}
} public static void main(String[] args) {
List<Integer> listOfNumbers = Arrays.asList(1, 2, 3, 4, 5);
forEach(listOfNumbers, (Integer i) -> System.out.println(i));
} }
d) Function<T, R>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function; public class Test { public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
} public static void main(String[] args) {
List<String> listOfStrings = Arrays.asList("lambdas", "in", "action");
List<Integer> l = map(listOfStrings, (String s) -> s.length());
for (Integer i : l) {
System.out.println(i);
}
} }
类型推断
1. 类型推断:同一个Lambda表达式就可赋予不同的函数式接口,只要它们的抽象方法签名能够兼容。
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> p = () -> 42;
Comparator<Apple> c1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
ToIntBiFunction<Apple, Apple> c2 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
BiFunction<Apple, Apple, Integer> c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
2. void兼容规则:如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。例如,虽然List.add方法返回boolean,但Consumer<T>的T->void仍然兼容。
Consumer<String> b = s -> list.add(s);
3. Object类:Object不是函数式接口。下面的代码无法编译。
Object o = () -> {System.out.println("Tricky example"); };
4. Lambda表达式类型省略
a) 参数类型省略:省略和不省略都可能更易读。
// 没有类型推断
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
// 有类型推断
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
b) 参数括号省略:当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));
使用外层变量
1. 外层变量限制:Lambda表达式只能使用显式声明为final或实际上是final的外层变量。与匿名类类似,但匿名类更严格(只能使用显式声明为final的外层变量)。
2. 限制原因
a) Lambda表达式在访问外层变量时,实际上是在访问它的副本,而不是访问原始变量。
b) 函数式编程不鼓励使用外层变量(更容易并行)。
3. 举例
a) 可正常运行
int number = 100;
Runnable r = () -> System.out.println(number);
new Thread(r).start();
b) 运行报错“local variables referenced from a lambda expression must be final or effectively final”
int number = 100;
Runnable r = () -> System.out.println(number);
new Thread(r).start();
number = 200;
方法引用
1. 方法引用:可以重复使用现有的方法定义,并像Lambda一样传递。
2. 方法引用优点:有时比Lambda表达式可读性更好。
3. 方法引用的种类
a) 指向静态方法的方法引用,例如Integer.parseInt()方法,写作Integer::parseInt。
b) 指向任意类型实例方法的方法引用,例如String.length()方法,写作String::length。
c) 指向现有对象的实例方法的方法引用,例如有一个局部变量apple有getWeight()实例方法,apple::getWeight。
d) 指向构造函数的方法引用,例如Date的构造方法,写作Date::new。
e) 针对构造函数、数组构造函数和父类调用(super-call)的一些特殊形式的方法引用。
4. 举例
a) Lambda表达式与方法引用对应
Lambda表达式 |
对应的方法引用 |
(Apple a) -> a.getWeight() |
Apple::getWeight |
() -> Thread.currentThread().dumpStack() |
Thread.currentThread()::dumpStack |
(str, i) -> str.substring(i) |
String::substring |
(String s) -> System.out.println(s) |
System.out::println |
() -> new Date() |
Date::new |
b) 指向现有对象的实例方法的方法引用
import java.util.Arrays;
import java.util.List; import static java.util.Comparator.comparing; public class Test { public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
// i.e. inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
// i.e. inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
inventory.sort(comparing(Apple::getWeight));
printApples(inventory);
} }
c) 指向构造函数的方法引用
import java.util.Date;
import java.util.function.Function;
import java.util.function.Supplier; interface TriFunction<T, U, V, R>{
R apply(T t, U u, V v);
} public class Test { public static void main(String[] args) {
Supplier<Date> s = Date::new; // i.e. () -> new Date()
Date d1 = s.get();
System.out.println(d1); Function<Long, Date> f = Date::new; // i.e. (Long l) -> new Date(l)
Date d2 = f.apply(0L);
System.out.println(d2); TriFunction<Integer, Integer, Integer, Date> tf = Date::new;
Date d3 = tf.apply(2000, 1, 1);
System.out.println(d3);
} }
复合Lambda表达式
1. 复合Lambda表达式:把多个简单的Lambda表达式复合成复杂的表达式,比如使用and、or复合。
2. 举例
a) Comparator复合
import java.util.Arrays;
import java.util.List; import static java.util.Comparator.comparing; public class Test { public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
inventory.sort(
comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getColor)
);
printApples(inventory);
} }
b) Predicate复合
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate; public class Test { public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
} public static void printApples(List<Apple> inventory) {
for (Apple apple : inventory) {
System.out.println(apple);
}
} public static void main(String[] args) {
List<Apple> inventory = Arrays.asList(
new Apple(100, "red"),
new Apple(110, "red"),
new Apple(190, "red"),
new Apple(170, "red"),
new Apple(100, "green"),
new Apple(120, "green"),
new Apple(160, "green"),
new Apple(180, "green")
);
Predicate<Apple> p = a -> "red".equals(a.getColor());
p = p.negate()
.and(a -> a.getWeight() > 150)
.or(a -> a.getWeight() <= 110);
List<Apple> newInventory = filterApples(inventory, p);
printApples(newInventory);
} }
c) 函数复合
import java.util.function.Function; public class Test { public static void main(String[] args) {
Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2; Function<Integer, Integer> h1 = f.andThen(g); // i.e. g(f(x))
int result1 = h1.apply(1);
System.out.println(result1); // Function<Integer, Integer> h2 = f.compose(g); // i.e. f(g(x))
int result2 = h2.apply(1);
System.out.println(result2); //
} }
作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。
3分钟看完Java 8——史上最强Java 8新特性总结之第一篇 函数式编程基础的更多相关文章
- 3分钟看完Java 8——史上最强Java 8新特性总结之第二篇 Stream API
目录 · 概况 · 切片(Slicing) · 映射(Mapping) · 匹配(Matching) · 查找(Finding) · 归约(Reducing) · 排序(Sorting) · 数值流( ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第四篇 其他新特性
目录 · 默认方法和静态方法 · 初步理解 · 应用模式 · 优先级问题 · Optional · CompletableFuture · 基本用法 · CompletableFuture与Strea ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第三篇 函数式编程技巧
目录 · 改写设计模式 · 策略模式(Strategy Pattern) · 模板方法模式(Template Method Pattern) · 观察者模式(Observer Pattern) · 责 ...
- 史上最强Java NIO入门:担心从入门到放弃的,请读这篇!
本文原题“<NIO 入门>,作者为“Gregory M. Travis”,他是<JDK 1.4 Tutorial>等书籍的作者. 1.引言 Java NIO是Java 1.4版 ...
- 金九银十,史上最强 Java 面试题整理。
以下会重新整理所有 Java 系列面试题答案.及各大互联网公司的面试经验,会从以下几个方面汇总,本文会长期更新. Java 面试篇 史上最全 Java 面试题,带全部答案 史上最全 69 道 Spri ...
- 史上最强Java开发环境搭建
在项目产品开发中,开发环境搭建是软件开发的首要阶段,也是必须阶段,只有开发环境搭建好了,方可进行开发,良好的开发环境搭建,为后续的开发工作带来极大便利. 对于大公司来说,软件开发环境搭建工作一般是由运 ...
- Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码
美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本. 为什么说是长期版本,看下面的官方发布的支持路线图表. 可以看出 Java 8 扩 ...
- c#代码 天气接口 一分钟搞懂你的博客为什么没人看 看完python这段爬虫代码,java流泪了c#沉默了 图片二进制转换与存入数据库相关 C#7.0--引用返回值和引用局部变量 JS直接调用C#后台方法(ajax调用) Linq To Json SqlServer 递归查询
天气预报的程序.程序并不难. 看到这个需求第一个想法就是只要找到合适天气预报接口一切都是小意思,说干就干,立马跟学生沟通价格. 不过谈报价的过程中,差点没让我一口老血喷键盘上,话说我们程序猿的人 ...
- 一文深入了解史上最强的Java堆内缓存框架Caffeine
它提供了一个近乎最佳的命中率.从性能上秒杀其他一堆进程内缓存框架,Spring5更是为了它放弃了使用多年的GuavaCache 缓存,在我们的日常开发中用的非常多,是我们应对各种性能问题支持高并发的一 ...
随机推荐
- HOSTNAME问题 和yum配置163源的操作 安装lsb_release,KSH,CSH
HOSTNAME 在 /etc/hosts 里添加一行 127.0.0.1 yourhostname yum配置 来自http://www.cnblogs.com/wutengbiao/p/41889 ...
- 【Selenium】【BugList3】firefox与Selenium版本不兼容,报: Message: Unsupported Marionette protocol version 2, required 3
环境信息:Windows7 64位 + python 3.6.5 + selenium 3.11.0 +pyCharm 1 #coding=utf-8 2 from selenium import w ...
- python模块:xml.dom.minidom
"""Simple implementation of the Level 1 DOM. Namespaces and other minor Level 2 featu ...
- python基本数据类型之字符串(四)
python基本数据类型之字符串(四) 判断方法 python中有一类用来判断字符串形式的方法,该类方法有两个特点:(1)方法名都是is开头(除了startswith和endswith):(2)返回值 ...
- 秒杀系统-service
在Dao层我们只完成了针对表的相关操作,包括写了接口方法和映射文件中的sql语句,并没有编写逻辑的代码,例如对多个Dao层方法的拼接,当我们用户成功秒杀商品时我们需要进行商品的减库存操作(调用Seck ...
- post网络请求坑
微信小程序开发中网络请求必不可少.GET.POST请求是最常用的.GET请求 POST请求的时候有好几个坑.我已经为大家填好了.
- 网络编程—端口分类调研和netstat命令
运输层的端口: 1.什么是端口?为甚要使用用端口? 进程的创建和撤销都是动态的,通信的一方几乎无法识别对方机器上的进程,我们需要利用目的主机提供的功能来识别终点. 所以为了解决上述问题,我们就在运输层 ...
- IPC_管道
1.管道特点: 1)单向数据通信 2)匿名管道-常用于(父子进程/有血缘关系的进程之间) 3)命名管道-常用于(无血缘关系进程之间通信) 4)提供一种流式服务(发送和接受不接受字节数的大小,可取任意大 ...
- Centos6.5安装中文支持和中文输入法
先来讲中文支持: 之前在网上查了不少资料,很多网友在网上都说,在shell命令下输入: # vi /etc/sysconfig/i18n 然后修改LANG="en_US.UTF-8& ...
- jdk8新特性(详解)
最近在复习外加看点面试题,jdk8的新特性虽然在项目用用到过一两个,准备系统的了解一下jdk8的常用新特性 一:Lambd表达式 也可称为闭包 引入函数式编程到Java中 为了使现有函 ...