前言
主要是对官方网页入门学习的一些截取,lambda厉害的地方就是改变了一种方式,将代码本身看成数据,数据就可以输入输出,函数式接口可以当做输入输出使得很多逻辑变成流水线一样的操作链,可能更符合处理事情的自然思维。
简介
lambda表达式是Java SE8引入的一个新的比较重要的特性。它可以用一种清晰简明的方式表示单方法接口。同时增强了集合的操作,使集合的迭代、过滤、数据提取变得简单。另外,新的并发特性提高了多核环境下的性能。
背景
匿名内部类
java中经常有只需要实现一次的类,常见的是UI中的点击动作,我们经常用匿名内部类的使用方式俩减少一个新类的定义。这种方式显得代码不够优雅,多行层层缩进的样式呈现出一种垂直样式。
16 JButton testButton = new JButton("Test Button");
17 testButton.addActionListener(new ActionListener(){
18 @Override public void actionPerformed(ActionEvent ae){
19 System.out.println("Click Detected by Anon Class");
20 }
21 })
函数式接口
在Java8中,只有一个方法的接口称之为“函数式接口”(functional interface)。
java中很多函数式接口使用匿名内部类,像UI事件中EventListener,Runnable和Comparator都是类似的例子。接下来,函数式接口可以用于lambda表达式。
lambda表达式语法
lambda表达式能将匿名内部类多行的代码精简为几行,简单的水平处理方式解决了匿名类存在的垂直问题。
一个lambda表达式分为三个部分:参数列表,符号,函数体。
Argument List | Arrow Token | Body |
---|---|---|
(int x, int y) | -> | x+y |
函数体可以是一个单一表达式,也可以是个语句块。如果是表达式,函数体计算表达式值并作为返回值。如果是语句块,函数体将被作为一个方法体,return语句将由这个匿名方法的调用者控制。break和continute不可以直接使用但可以在循环体中使用。如果函数体产生一个结果,每个控制路径必须返回什么或抛出异常。
三个列子
(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }
前两个表达式形式的直接返回计算结果,第三个将执行函数体的操作打印字符串s,什么都不返回。
这种形式下,代码将被看成数据,lambda表达式可以作为参数来传递。目标类型能在很多上下文中使用,比如:
- Variable declarations
- Assignments
- Return statements
- Array initializers
- Method or constructor arguments
- Lambda expression bodies
- Conditional expressions ?:
- Cast expressions
lambda表达式的使用
官网上有很清晰的例子讲解lambda的使用对代码的精简化效果。
java.util.function
包中已经为开发者准备一些现成的函数式接口
- Predicate: A property of the object passed as argument
- Consumer: An action to be performed with the object passed as argument
- Function: Transform a T to a U
- Supplier: Provide an instance of a T (such as a factory)
- UnaryOperator: A unary operator from T -> T
- BinaryOperator: A binary operator from (T, T) -> T
比如Function的函数式接口
Function接口只要一个apply方法:
public R apply(T t){ }
接收一个泛型参数输入并输出一个泛型。就可以根据输入t计算出想要的结果R。
链接和过滤器
Chaining and Filters
除了对集合中使用forEach,还可以将方法链接起来。filter方法可以将一个Predicate接口作为参数。
List<Person> pl = Person.createShortList();
pl.stream().filter(search.getCriteria("allDraftees"))
.forEach(Person::printEasternName);
这种特性很有用,但当有个了一个很好的for-loop时,通过移动迭代特征库,允许程序员有更大的优化代码的空间。解释两个概念:
-
Laziness:懒惰指的是只处理需要处理时要处理的对象。上面的示例中,循环是“懒惰的”,因为它只通过列表过滤后剩下的两个对象循环。代码应该更有效,因为最终处理步骤只发生在选定的对象上。
-
Eagerness:渴望:对列表中的每一个对象执行操作的代码被认为是“热切”的。例如,为了最后要执行的两个对象一个for循环遍历整个列表,被认为是一种“渴望”的方法。
简单说来就是Laziness将循环限定在了符合条件的对象上,而不用为了找到这些对象循环遍历整个集合。
stream方法
stream方法经常在filter和loop开始之前调用,该方法将集合作为输入,并返回一个java.util.stream.Stream。stream代表一个元素序列,可以链接各种方法。默认情况下 ,一旦袁旭被消费了,在stream中就无法再次获取它们了。在一个特定的stream中一系列操作链只能发生一次。另外,stream可以由方法调用者决定是串行(默认)还是并行。
如前所述,流在使用后被回收。因此,集合中的元素不能用流进行更改或变成可变。但是,如果您希望保留从链接操作返回的元素,可以将它们保存到一个新集合中。
// Make a new list after filtering.
List<Person> pilotList = pl
.stream()
.filter(search.getCriteria("allPilots"))
.collect(Collectors.toList());
用Map进行计算
map方法常用于过滤器。方法从类获取一个属性并用它做一些事情。
34 // Get sum of ages
35 System.out.println("\n== Calc New Style ==");
36 long totalAge = pl
37 .stream()
38 .filter(search.getCriteria("allPilots"))
39 .mapToInt(p -> p.getAge())
40 .sum();
41
42 // Get average of ages
43 OptionalDouble averageAge = pl
44 .parallelStream()
45 .filter(search.getCriteria("allPilots"))
46 .mapToDouble(p -> p.getAge())
47 .average();
如何在AndroidStudio中使用Lambda
Jdk要升级1.8或者以上
AndroidStudio3.0已经能直接支持Java8的某些特性了,要注意的是gradle要升级到4.+,android gradle plugin要相应的在3.+以上,这在官网中都有介绍Use Java 8 Language Features。还可以使用retrolambda来支持。
最好不用jack了,goolge也已经弃了Jack&Jill这对CP了Google 又弃坑了,Jack+Jill vs. javac+dx。
网友评论