Java 8 lambdas读书笔记

目录 [−]

  1. 第二章 Lambda表达式 (lambda expression)
    1. Lambda表达式有很多简写方法, 如
    2. 使用值
    3. Functional Interfaces
    4. 类型推断
  2. Stream
    1. 多stream链式处理
    2. Higher-Order Functions
  3. 库Libraries
    1. 一个lambda应用的例子
    2. 基本类型
    3. 重载
    4. @FunctionalInterface
    5. Binary Interface Compatibility
    6. Default Method
    7. 继承中覆盖default method
    8. 多继承
    9. 三条原则
    10. 接口可以定义静态方法!
    11. Optional
  4. Advanced Collections and Collectors
    1. 方法引用
    2. 元素排序
    3. Collector
      1. 转换成其它集合类
      2. 得到单一值
      3. 分区
      4. 分组
      5. 字符串拼装
      6. 组合Collector
      7. Collector复杂应用
  5. 数据并行处理
    1. Parallelism vs. Concurrency
    2. Parallel Stream Operations
    3. Parallel Array Operations
  6. 测试, 调试和重构
    1. point lambdafication
    2. 测试
    3. lazy and debugging
  7. 设计和架构原则
    1. Lambda-Enabled Design Patterns
      1. Command
      2. Strategy
      3. Template Method
    2. Lambda-Enabled Domain-Specific Languages
    3. Lambda-Enabled SOLID Principles
  8. Lambda-Enabled Concurrency

Java 8 Lambdas是O'Reilly 2014年3月出版的一本介绍最新Java Lambda技术的书。网上评价也不错。正好我需要找一本全面介绍Java Lambda数据, 来整理一下我脑海中的关于Java Lambda非系统的知识点。

Lambda对于我来讲,并不是一个很新的技术。因为微软很早就在C#中加入了Lambda的特性。 而Java Lambda的语法和C#的语法非常的相像。 这篇文章算是对Java Lambda语法的一个整理。

这本书一共十个章节。 第一章是简单介绍,没有实质内容。 第二章主要介绍了Java Lambda的语法。第三章介绍了Stream接口。第四章介绍了Functional Interface。第五章 首先介绍了方法引用,然后主要介绍了集合类新的Stream处理方式。 第六章 介绍了数据的并行处理。 第七章介绍了测试,调试和重构技术。第八章介绍了使用lambda的设计和架构原则。
第九章介绍了并发编程中的Lambda的应用。 第十章 前瞻,无实质内容

第二章 Lambda表达式 (lambda expression)

介绍Lambda表达式一般都以匿名函数为例子, 说明Lambda表达式的简介。 例子一般为两个: 一个是Swing的事件处理, 如本书中举例, 另外一个以Runnable接口的实现为例。

1
button.addActionListener(event -> System.out.println("button clicked"));

Lambda表达式有很多简写方法, 如

1
2
3
4
5
6
7
8
Runnable noArguments = () -> System.out.println("Hello World");
ActionListener oneArgument = event -> System.out.println("button clicked");
Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};
BinaryOperator<Long> add = (x, y) -> x + y;
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

无参数, 一个参数, 方法体只有一个表达式的情况,多个表达式的情况。 多个参数的情况。 定义表达式的参数类型等。

Lambda表达式的target type是指表达式出现的上下文的类型, 如上面的本地变量noArguments的类型, 或者作为方法参数调用时的方法参数的类型。

使用值

内部类是会遇到final 变量的情况。 lambda稍微放松了一些。 你可以使用非final的变量,但是你也不能在Lambda中更改它的值。

Functional Interfaces

没有上下文的Lambda表达式是没有意义的。一个Lambda表达式的target type总是一个Functional interface.
一些重要的Functional interface:

Interface nameArgumentsReturnsExample
PredicateTbooleanHas this album been released yet?
ConsumerTvoidPrinting out a value
Function<T,R>TRGet the name from an Artistobject
SupplierNoneTA factory method
UnaryOperatorTTLogical not (!)
BinaryOperator(T, T)TMultiplying two numbers (*)

Functional interface只能拥有一个显示声明的abstract方法。 可以包含多个default method。所以Functional interface也叫Single Abstract Method (SAM) interfaces。

类型推断

Java 8相比Java 7对类型推断更智能, 一般lambda 表达式中的参数类型可以省略,可以根据上下文推断得出。

Stream

为了充分利用Lambda, Java 8为集合框架增加了流式处理。
Stream接口提供了很多集合处理的方法。
调用集合类的stream方法:

1
2
3
long count = allArtists.stream()
.filter(artist -> artist.isFrom("London"))
.count();

filter是lazy的,不会马上执行一个loop, count是eager的会执行一次loop。 所以上面的只执行一次loop。勿用担心。

判断一个操作是lazy还是eager的方法是: 看返回结果。 返回结果还是stream, 那么它就是lazy的。如果返回其它值或者void, 它就是eager的。

整个处理有点像builder设计模式。

一些stream操作:

  • collect(toList()): 得到一个List
  • map: 将值映射成另外一个值
  • flatMap: 将值替换成stream并将所有的stream合并成一个
  • max and min
  • reduce: 将得到的集合的值处理成单一的结果
1
2
3
int count = Stream.of(1, 2, 3)
.reduce(0, (acc, element) -> acc + element);
assertEquals(6, count);

多stream链式处理

1
2
3
4
5
6
7
8
List<Artist> musicians = album.getMusicians()
.collect(toList());
List<Artist> bands = musicians.stream()
.filter(artist -> artist.getName().startsWith("The"))
.collect(toList());
Set<String> origins = bands.stream()
.map(artist -> artist.getNationality())
.collect(toSet());

可以写成

1
2
3
4
Set<String> origins = album.getMusicians()
.filter(artist -> artist.getName().startsWith("The"))
.map(artist -> artist.getNationality())
.collect(toSet());

Higher-Order Functions

高位函数只它的参数可以是函数或者返回结果是函数。 map就是一个高位函数。它的mapper参数就是一个函数。

库Libraries

Java 8另一个重大改变是default method和接口的静态方法。

一个lambda应用的例子

我们输出log的时候考虑性能:

1
2
3
if (logger.isDebugEnabled()) {
logger.debug("Look at this: " + expensiveOperation());
}

我们可以实现下面的logger:

1
2
3
4
5
6
logger.debug(() -> "Look at this: " + expensiveOperation());
public void debug(Supplier<String> message) {
if (isDebugEnabled()) {
debug(message.get());
}
}

基本类型

为基本类型特制的stream如LongStream.

重载

java无法区分的重载则编译失败

1
2
3
4
5
6
7
8
9
10
overloadedMethod((x) -> true);
private interface IntPredicate {
public boolean test(int value);
}
private void overloadedMethod(Predicate<Integer> predicate) {
System.out.print("Predicate");
}
private void overloadedMethod(IntPredicate predicate) {
System.out.print("IntPredicate");
}

@FunctionalInterface

Binary Interface Compatibility

兼容性问题, 以及default method解决方案

Default Method

Collection增加了stream, Iterable增加了forEach.

1
2
3
4
5
default void forEach(Consumer<? super T> action) {
for (T t : this) {
action.accept(t);
}
}

继承中覆盖default method

菱形继承的问题: class win

多继承

可能会编译出错。 如果继承的几口中有相同的default method,必须显式继承。

三条原则

  1. Any class wins over any interface
  2. Subtype wins over supertype
  3. No rule 3 不满足上面两条则必须显示声明

接口可以定义静态方法!

Stream.of

Optional

1
2
3
4
5
6
7
8
9
10
11
Optional<String> a = Optional.of("a");
assertEquals("a", a.get());
Optional emptyOptional = Optional.empty();
Optional alsoEmpty = Optional.ofNullable(null);
assertFalse(emptyOptional.isPresent());
// a is defined above
assertTrue(a.isPresent())
assertEquals("b", emptyOptional.orElse("b"));
assertEquals("c", emptyOptional.orElseGet(() -> "c"));

Advanced Collections and Collectors

方法引用

Classname::methodName
Classname::new
String[]::new

元素排序

Collector

转换成其它集合类

得到单一值

分区

分组

字符串拼装

组合Collector

Collector复杂应用

数据并行处理

Parallelism vs. Concurrency

Concurrency 两个task处理时有重叠的时刻。
Parallelism是只两个task同时处理。 发生在多核的机器上。

Parallel Stream Operations

collection.stream().parallel()或者collection.parallelStream。
并不是parallelStream更快。 考虑你的数据量。 可能在很大的数据量的情况下才去用parallelStream。

Parallel Array Operations

Arrays类

NameOperation
parallelPrefixCalculates running totals of the values of
parallelSetAllUpdates the values in an array using a lam
parallelSortSorts elements in parallel

测试, 调试和重构

point lambdafication

logger例子。
ThreadLocal例子:

1
2
3
4
5
ThreadLocal<Album> thisAlbum = new ThreadLocal<Album> () {
@Override protected Album initialValue() {
database.lookupCurrentAlbum();
}
};
1
2
ThreadLocal<Album> thisAlbum
= ThreadLocal.withInitial(() -> database.lookupCurrentAlbum());

将类似代码(并不是重复代码)重构成lambda表达式。

测试

lambda表达式没有名字,不能直接测试。
可以整体测试。

lazy and debugging

使用peak去输出对象。

设计和架构原则

SOLID原则: 单一功能、开闭原则、里氏替换、接口隔离以及依赖反转

Lambda-Enabled Design Patterns

Command

Strategy

Template Method

Lambda-Enabled Domain-Specific Languages

Lambda-Enabled SOLID Principles

Lambda-Enabled Concurrency