资讯专栏INFORMATION COLUMN

《java 8 实战》读书笔记 -第三章 Lambda表达式

whinc / 3604人阅读

摘要:利用前面所述的方法,这个例子可以用方法引用改写成下面的样子构造函数引用对于一个现有构造函数,你可以利用它的名称和关键字来创建它的一个引用。

第三章 Lambda表达式 函数式接口
函数式接口就是只定义一个抽象方法的接口,哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
常用函数式接口

函数描述符
函数式接口的抽象方法的签名称为函数描述符。
在哪里可以使用Lambda?

只有在需要函数式接口的时候才可以传递Lambda
下哪些是使用Lambda表达式的有效方式?
(1)

execute(() -> {});
public void execute(Runnable r){ 
r.run(); 
} 

(2)

return () -> "Tricky example ;-)"; 

(3)

Predicate p = (Apple a) -> a.getWeight();

答案:只有1和2是有效的
第一个例子有效,是因为Lambda() -> {}具有签名() -> void,这和Runnable中的
抽象方法run的签名相匹配。请注意,此代码运行后什么都不会做,因为Lambda是空的!
第二个例子也是有效的。事实上,fetch方法的返回类型是Callable
Callable基本上就定义了一个方法,签名是() -> String,其中T被String代替
了。因为Lambda() -> "Trickyexample;-)"的签名是() -> String,所以在这个上下文
中可以使用Lambda。
第三个例子无效,因为Lambda表达式(Apple a) -> a.getWeight()的签名是(Apple) ->
Integer,这和Predicate:(Apple) -> boolean中定义的test方法的签名不同。

@FunctionalInterface又是怎么回事
这个标注用于表示该接口会设计成一个函数式接口,@FunctionalInterface不是必需的,它就像是@Override标注表示方法被重写了。
Java 7中的带资源的try语句

它已经简化了代码,因为你不需要显式地关闭资源了.

public static String processFile() throws IOException { 
  try (BufferedReader br = 
  new BufferedReader(new FileReader("data.txt"))) { 
  return br.readLine(); 
  } 
} 
函数式接口:Predicate断言
java.util.function.Predicate接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
函数式接口:Consumer
java.util.function.Consumer定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。
函数式接口:Function

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

@FunctionalInterface 
public interface Function{ 
 R apply(T t); 
} 
public static  List map(List list, 
 Function f) { 
 List result = new ArrayList<>(); 
 for(T s: list){ 
 result.add(f.apply(s)); 
 } 
 return result; 
} 
// [7, 2, 6] 
List l = map( 
 Arrays.asList("lambdas","in","action"), 
 (String s) -> s.length() 
 ); 
避免自动装箱、拆箱
一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction等。Function接口还有针对输出参数类型的变种:ToIntFunction、IntToDoubleFunction等。
关于异常
请注意,任何库中的函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中。
目标类型
Lambda表达式需要的类型称为目标类型。(即对应的函数式接口)
类型推断

你还可以进一步简化你的代码。Java编译器会从上下文(目标类型)推断出用什么函数式接
口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通
过目标类型来得到。这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可
以在Lambda语法中省去标注参数类型。换句话说,Java编译器会像下面这样推断Lambda的参数
类型:

List greenApples = 
filter(inventory, a -> "green".equals(a.getColor())); 
方法引用
为三种不同类型的Lambda表达式构建方法引用的办法:
List str = Arrays.asList("a","b","A","B"); 
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2)); 

Lambda表达式的签名与Comparator的函数描述符兼容。利用前面所述的方法,这个例子可
以用方法引用改写成下面的样子:

List str = Arrays.asList("a","b","A","B"); 
str.sort(String::compareToIgnoreCase); 
构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:
ClassName::new。

构造函数引用
要怎么样才能对具有三个参数的构造函数,比如Color(int, int, int),使用构造函数引用呢?
答案:你看,构造函数引用的语法是ClassName::new,那么在这个例子里面就是Color::new。但是你需要与构造函数引用的签名匹配的函数式接口。但是语言本身并没有提供这样的函数式接口,你可以自己创建一个:

public interface TriFunction{ 
 R apply(T t, U u, V v); 
} 

现在你可以像下面这样使用构造函数引用了:

TriFunction colorFactory = Color::new; 
Comparator 类内部comparing实现

comparing 方法一
查看 Comparator 类内部实现,还有一个 comparing 方法,实现如下,

   public static > Comparator comparing(
           Function keyExtractor)
   {
       Objects.requireNonNull(keyExtractor);
       return (Comparator & Serializable)
           (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
   }

其返回值是 (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); 一个lambda表达式,也就是一个Compator
eg:

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

comparing 方法二

   public static  Comparator comparing(
           Function keyExtractor,
           Comparator keyComparator)
   {
       Objects.requireNonNull(keyExtractor);
       Objects.requireNonNull(keyComparator);
       return (Comparator & Serializable)
           (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                             keyExtractor.apply(c2));
   }

和comparing 方法一不同的是 该方法多了一个参数 keyComparator ,keyComparator 是创建一个自定义的比较器。

Collections.sort(employees, Comparator.comparing(
               Employee::getName, (s1, s2) -> {
                   return s2.compareTo(s1);
               }));
比较器复合

逆序

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

比较器链
thenComparing方法就是做这个用的

inventory.sort(comparing(Apple::getWeight) 
.reversed().thenComparing(Apple::getCountry)); 
谓词复合(断言复合)

谓词接口包括三个方法:negate、and和or,让你可以重用已有的Predicate来创建更复
杂的谓词。比如,你可以使用negate方法来返回一个Predicate的非,比如苹果不是红的:

Predicate notRedApple = redApple.negate(); 

你可能想要把两个Lambda用and方法组合起来,比如一个苹果既是红色又比较重:

Predicate redAndHeavyApple = 
redApple.and(a -> a.getWeight() > 150);

你可以进一步组合谓词,表达要么是重(150克以上)的红苹果,要么是绿苹果:

Predicate redAndHeavyAppleOrGreen = 
redApple.and(a -> a.getWeight() > 150) 
.or(a -> "green".equals(a.getColor())); 

这一点为什么很好呢?从简单Lambda表达式出发,你可以构建更复杂的表达式,但读起来仍然和问题的陈述差不多!请注意,and和or方法是按照在表达式链中的位置,从左向右确定优先级的。因此,a.or(b).and(c)可以看作(a || b) && c。

函数复合

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

andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数;用compose方法,先把给定的函数用作compose的参>数里面给的那个函数,然后再把函数本身用于结果。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77537.html

相关文章

  • java 8 实战读书笔记 -第八章 重构、测试和调试

    摘要:通常,这种模式是通过定义一个代表处理对象的抽象类来实现的,在抽象类中会定义一个字段来记录后续对象。工厂模式使用表达式第章中,我们已经知道可以像引用方法一样引用构造函数。 一、为改善可读性和灵活性重构代码 1.改善代码的可读性 Java 8的新特性也可以帮助提升代码的可读性: 使用Java 8,你可以减少冗长的代码,让代码更易于理解 通过方法引用和Stream API,你的代码会变得更...

    gclove 评论0 收藏0
  • java 8 实战读书笔记 -第十四章 函数式编程的技巧

    摘要:但是,最好使用差异化的类型定义,函数签名如下其实二者说的是同一件事。后者的返回值和初始函数的返回值相同,即。破坏式更新和函数式更新的比较三的延迟计算的设计者们在将引入时采取了比较特殊的方式。四匹配模式语言中暂时并未提供这一特性,略。 一、无处不在的函数 一等函数:能够像普通变量一样使用的函数称为一等函数(first-class function)通过::操作符,你可以创建一个方法引用,...

    nemo 评论0 收藏0
  • Java 8 函数式编程」读书笔记——lambda达式

    摘要:本文是函数式编程第二章的读书笔记。的语法简化了使用匿名内部类时的模板代码,让程序员专注于编写想要执行的行为,也让代码更加简洁易读。中最重要的函数接口类型推断为新成员表达式提供了类型推断的支持,在不需要声明参数类型的表达式中表现的有为明显。 本文是「Java 8 函数式编程」第二章的读书笔记。 Lambda引入的变化 Lambda表达式,是一种紧凑的、传递行为的方式,从编程思想上来讲,...

    lx1036 评论0 收藏0
  • Java8实战》-读书笔记第一章(01)

    摘要:依旧使用刚刚对苹果排序的代码。现在,要做的是筛选出所有的绿苹果,也许你会这一个这样的方法在之前,基本上都是这样写的,看起来也没什么毛病。但是,现在又要筛选一下重量超过克的苹果。 《Java8实战》-读书笔记第一章(01) 最近一直想写点什么东西,却不知该怎么写,所以就写写关于看《Java8实战》的笔记吧。 第一章内容较多,因此打算分几篇文章来写。 为什么要关心Java8 自1996年J...

    codeGoogle 评论0 收藏0
  • Java8实战》-第四章读书笔记(引入流Stream)

    摘要:内部迭代与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。流只能遍历一次请注意,和迭代器类似,流只能遍历一次。 流(Stream) 流是什么 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了!我会在后面的笔记中...

    _ivan 评论0 收藏0
  • Java8实战》-读书笔记第一章(02)

    摘要:实战读书笔记第一章从方法传递到接着上次的,继续来了解一下,如果继续简化代码。去掉并且生成的数字是万,所消耗的时间循序流并行流至于为什么有时候并行流效率比循序流还低,这个以后的文章会解释。 《Java8实战》-读书笔记第一章(02) 从方法传递到Lambda 接着上次的Predicate,继续来了解一下,如果继续简化代码。 把方法作为值来传递虽然很有用,但是要是有很多类似与isHeavy...

    lushan 评论0 收藏0

发表评论

0条评论

whinc

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<