发布时间:2023-02-06 文章分类:编程知识 投稿人:赵颖 字号: 默认 | | 超大 打印

读Java实战(第二版)笔记02_行为参数化Lambda表达式

1.行为参数化

1.1.处理频繁变更的需求的一种软件开发模式

1.1.1.不管你做什么,用户的需求肯定会变

1.1.2.可让代码更好地适应不断变化的要求,减轻未来的工作量

1.2.一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力

1.2.1.类似于在内联“传递代码”

1.2.2.重复使用同一个方法,给它不同的行为来达到不同的目的

1.2.2.1.DRY(Don't Repeat Yourself,不要重复自己)

1.2.2.2.例如:把迭代要筛选的集合的逻辑与对集合中每个元素应用的行为区分开来

1.3.传递代码就是将新行为作为参数传递给方法

1.3.1.在Java 8之前可以用匿名类

1.3.1.1.匿名类和局部类(块中定义的类)差不多,但匿名类没有名字

1.3.1.2.GUI应用程序中经常使用匿名类来创建事件处理器对象

1.3.1.3.声明很多只要实例化一次的类

1.3.2.以用不同行为进行参数化的方法,包括排序、线程和GUI处理

2.Lambda表达式

2.1.基本语法

2.1.1.块-风格的Lambda

2.1.1.1.(parameters) -› { statements; }

2.1.2.表达式-风格的Lambda

2.1.2.1.(parameters) -› expression

2.2.没有声明名称的方法

2.3.和匿名类一样,也能作为参数传递给一个方法

2.4.允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例

2.5.一种简洁的可传递匿名函数:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表

2.5.1.匿名

2.5.2.函数

2.5.2.1.有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表

2.5.3.传递

2.5.3.1.可以作为参数传递给方法或存储在变量中

2.5.4.简洁

2.5.4.1.无须像匿名类那样写很多模板代码

2.6.方法调用的返回值为空时,Java语言规范有一条特殊的规定,不需要使用括号环绕返回值为空的单行方法调用

2.7.捕获Lambda允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样

2.7.1.可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量

2.7.2.局部变量必须显式声明为final,或事实上是final

2.7.2.1.只能捕获指派给它们的局部变量一次

2.7.2.2.捕获实例变量可以被看作捕获最终局部变量this

2.7.2.3.不能修改定义Lambda的方法的局部变量的内容。这些变量必须是隐式最终的

2.7.3.实例变量都存储在堆中

2.7.3.1.堆是在线程之间共享的

2.7.4.局部变量则保存在栈上

2.7.4.1.允许捕获可改变的局部变量,就会引发造成线程不安全的新的可能性

2.7.4.2.在访问自由局部变量时,实际上是在访问它的副本,而不是访问基本变量

2.7.5.不鼓励你使用改变外部变量的典型命令式编程模式

2.8.Lambda是对值封闭,而不是对变量封闭

2.9.闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量

2.9.1.可以访问和修改其作用域之外的变量

3.匿名内部类

3.1.也可以完成Lambda表达式同样的事情,比较笨拙:需要提供一个实现,然后再直接内联将它实例化

4.方法引用

4.1.仅调用特定方法的Lambda的一种快捷写法

4.1.1.更易读

4.1.2.更自然

4.2.仅涉及单一方法的Lambda的语法糖

4.2.1.同样的事情时要写的代码更少

4.3.如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它

4.4.指向静态方法的方法引用

4.4.1.例如Integer的parseInt方法,写作Integer::parseInt

4.5.指向任意类型实例方法的方法引用

4.5.1.例如String的length方法,写作String::length

4.6.指向现存对象或表达式实例方法的方法引用

4.6.1.例如你有一个局部变量expensive Transaction保存了Transaction类型的对象,它提供了实例方法getValue,那你就可以这么写expensive-Transaction::getValue

4.7.构造函数引用

4.7.1.对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName::new

4.7.2.不将构造函数实例化却能够引用它

4.7.3.语言本身并没有提供的函数式接口,可以自己创建一个

5.函数式接口

5.1.只定义一个抽象方法的接口

5.1.1.很多默认方法,只要接口只定义了一个抽象方法,仍然是一个函数式接口

5.1.2.默认方法不是抽象方法

5.2.函数式接口的抽象方法的签名称为函数描述符

5.2.1.基本上就是Lambda表达式的签名

5.3.@FunctionalInterface

5.3.1.这个标注用于表示该接口会设计成一个函数式接口,因此对文档来说非常有用

5.3.2.不是必需的,但对于为此设计的接口而言,使用它是比较好的做法

5.4.java.util.function包中新的函数式接口

5.4.1.Predicate‹T›接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean

5.4.1.1.谓词(即一个返回boolean值的函数)

5.4.2.Consumer‹T›接口定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)

5.4.3.Function‹T, R›接口定义了一个叫作apply的抽象方法,它接受泛型T的对象,并返回一个泛型R的对象

5.4.3.1.配了andThen和compose两个默认方法,它们都会返回Function的一个实例

5.4.4.Supplier‹T›

5.4.5.BinaryOperator‹T›

5.5.函数式接口中的任何一个都不允许抛出受检异常(checked exception)

5.5.1.定义一个自己的函数式接口,并声明受检异常

5.5.2.把Lambda包在一个try/catch块中

5.5.2.1.环绕执行(execute around)模式

6.装箱(boxing)

6.1.将基本类型转换为对应的引用类型的机制

7.拆箱(unboxing)

7.1.将引用类型转换为对应的基本类型

8.自动装箱机制

8.1.装箱和拆箱操作是自动完成的

9.特殊处理

9.1.IntPredicate

9.2.DoublePredicate

9.3.ToIntFunction‹T›

9.4.IntConsumer

9.5.LongBinaryOperator

9.6.IntFunction

9.7.IntToDoubleFunction

10.目标类型

10.1.Lambda的类型是从使用Lambda的上下文推断出来的

10.2.上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型

10.3.同一个Lambda表达式就可以与不同的函数式接口联系起来,只要它们的抽象方法签名能够兼容

10.4.Lambda表达式的上下文是Object(目标类型)。但Object不是一个函数式接口。

10.4.1.你可以把目标类型改成Runnable,它的函数描述符是() -› void

10.5.为了消除显式的二义性,你可以对Lamda进行强制类型转换

10.6.有时候显式写出类型更易读,有时候去掉它们更易读