您的位置:首页 > 编程语言 > Java开发

《Java in Action》-1 第3章 Lambda表达式

2016-10-02 23:45 405 查看

第3章 Lambda表达式

3.1 Lambda管中窥豹

Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但他们有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

Lamdba表达式有三个部分:

参数列表 + 箭头 + Lambda主体

基本语法:

(parameters) -> expression



(parameters) -> {statements; }

3.2 在哪里以及如何使用Lambda

3.2.1 函数式接口

函数式接口就是只定义一个抽象方法的接口。(注意只有一个)

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

因为Runnable是一个只定义了一个抽象方法run的函数式接口,所以可以以下这样:

Runnable a = () -> System.out.println("hi 1");

a.run();

3.2.2 函数描述符

函数式接口的的抽象方法的签名基本上就是Lambda表达式的签名。将这种抽象方法叫作函数描述符。

3.3 把Lambda付诸实践:环绕执行模式

3.4使用函数式接口

函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名。函数式接口的抽象方法的签名称为函数描述符。

Java  api中已经有了几个函数式接口,包括Comparable、Runnable和Callable。

Java 8引入了几个新的函数式接口,包括Predicate、Comsumer和Function。

3.4.1 Predicate

java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。

public class PredicateTest {

private <T> List<T> choiceList(List<T> lists,Predicate<T> pres) {
ArrayList<T> arrayList = new ArrayList<>();
for(T t : lists) {
if(pres.test(t)) {
arrayList.add(t);
}
}
return arrayList;
}

public static void main(String[] args) {
PredicateTest predicateTest = new PredicateTest();
List<Integer> choiceList = predicateTest.choiceList(Arrays.asList(1,2,3,4,5),
(Integer i) -> { if(i > 3) {return true;}else{return false;}});
System.out.println(choiceList);
}
}


3.4.2 Consumer

java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。

public class ConsumerTest {

private <T> void foreachList(List<T> lists,Consumer<T> consm)	{
for(T t : lists){
consm.accept(t);
}
}

public static void main(String[] args) {
ConsumerTest consumerTest = new ConsumerTest();
consumerTest.foreachList(Arrays.asList("Consumer Interface 1","Consumer 2","Consumer 3"),
(String s) -> System.out.println(s));

}
}


3.4.3 Function

java.util.function.Function<T,R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。

public class FunctionTest {

private <T,R> List<R> functionLists(List<T> lists , Function<T, R> funs){
ArrayList<R> arrayList = new ArrayList<>();
for(T t : lists) {
R apply = funs.apply(t);
arrayList.add(apply);
}
return arrayList;
}

public static void main(String[] args) {
FunctionTest functionTest = new FunctionTest();
List<Integer> functionLists = functionTest.functionLists(Arrays.asList("a","bc","edf"),
(String s) -> s.length());
System.out.println(functionLists);
}

}






3.5 类型检查、类型推断以及限制

3.5.1 类型检查

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

3.5.2 同样的Lambda,不同的函数式接口

*特殊的void兼容规则

如果一个Lambda的主体是一个语句表达式,它就和一个返回void的函数描述符兼容。

3.5.3 类型推断

Java编译器会从上下文(目标类型)推断出用什么函数接口来配合Lambda表达式,这意味着他也可以推断适合Lambda的签名,因为函数描述符可以通过目标类型来得到。这样,编译器可以了解Lambda表达式的参数类型,可以在Lambda语法中省去标注参数类型。

可以这样:

List<Apple> greenApples = 

filter(inventory, a -> "green".equals(a.getColor())); 

Comparator<Apple> c = (a1,a2) -> a1.getWeight().compareTo(a2.getWeight());

3.5.4 使用局部变量

Lambda表达式也允许使用自由变量,就像匿名类一样,他们被称作捕获Lambda。

Lambda可以没有限制地捕获实例变量和静态变量,但局部变量必须显示声明为final或事实上是final。

int portNumber = 1337;

Runnable r = () -> System.out.println(portNumber);

*闭包

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

3.6 方法引用

方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递他们。

先前:

inventory.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

之后:

inventory.sort(comparing(Apple::getWeight));

Apple :: getWeight就是引用Apple类中定义的方法getWeight。

3.6.1 管中窥豹

*如何构建方法引用

(1)指向静态方法的方法引用

(例如Integer的parseInt方法,写作Integer :: parseInt)。

(2)指向任意类型实例方法的方法引用

(例如String的length方法,写作String :: length)

(3)指向现有对象的实例方法的方法引用

()

例子:

List<String> str = Arrays.asList("a","b","A","b");

str.sort((s1,s2) -> s1.compareToIgnoreCase(s2));

变成:

str.sort(String :: compareToIgnoreCase);

3.6.2 构造函数引用

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

Supplier<Apple> c1 = () -> new Apple();

Apple a1 = c1.get();

等价于:

Supplier<Apple> c1 = Apple ::
new;

Apple a1 = c1.get();

3.7 Lambda和方法引用实战

想要实现的最终解决方案是这样的:

3.8 复合Lambda表达式的有用方法

3.8.1 比较器复合

Comparator<Apple> c = Comparator.comparing(Apple
:: getWeight);

1.逆序

inventory.sort(comparing(Apple
:: getWeight).reversed());//按重量递减排序。

2.比较器链

inventory.sort(comparing(Apple :: getWeight)

.reversed()

.thenComparing(Apple :: getCountry)

);

3.8.2 谓词复合

谓词接口包括三个方法:negate、and和or,让你可以重用已有的Predicate来创建更复杂的谓词。

使用negate方法来返回一个Predicate的非:

Predicate<Apple> notRedApple = redApple.negate();

3.8.3 函数复合

可以把Function接口所代表的Lambda表达式复合起来,Function接口为此配置了andThen和compose两个默认方法,它们都会返回Function的一个实例。

Function<Integer,Integer> f = x -> x + 1;

Function<Integer,Integer> g = x -> x * 2;

Function<Integer,Integer> h = f.andThen(g);//数学上写作g(f(x))

int result = h.apply(1);//返回4

Function<Integer,Integer> f = x -> x + 1;

Function<Integer,Integer> g = x -> x * 2;

Function<Integer,Integer> h = f.compose(g);//数学上写作f(g(x))

int result = h.apply(1);//返回4

3.9 数学中的类似思想

3.10 小结

*Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

*Lambda表达式让你可以简洁地传递代码

*函数式接口就是仅仅声明了一个抽象方法的接口

*只有在接受函数式接口的地方才可以使用Lambda表达式

*Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。

*Java 8 自带一些常用的函数式接口,放在java.util.function包里,包括Predicate<T>,Function<T,R>,Supplier<T>,BinaryOperator<T>。

*为了避免装箱操作,对通用函数式接口的原始类型特化:IntPredicate,IntToLongFunction等。

*Lambda表达式所需要代表的类型称为目标类型。

*方法引用让你重复使用现有的方法实现并直接传递它们。

*Comparator、Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: