java8实战读书笔记:Lambda表达式语法与函数式编程接口(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

(5)错误。因为"IronMan"是一个表达式,并不是一个语句,故不能使用{}修饰,应修改为 (String s) -> “IronMan”。如果使用匿名类(接口名统一使用IDemoLambda)表示如下:

new IDemoLambda() {

public String test(String s) {

return “IronMan”;

}

}

2、初步接触函数式接口

在java8中,一个接口如果只定义了一个抽象方法,那这个接口就可以称为函数式接口,就可以使用lambda表达式来简化程序代码。Lambda表达式可以直接赋值给变量,也可以直接作为参数传递给函数,示例如下:

public static void startThread(Runnable a) {

(new Thread(a)).start();

}

public static void main(String[] args) {

// lambda表达式可以直接赋值给变量,也可以直接以参数的形式传递给方法、

Runnable a = () -> {

System.out.println(“Hello World,Lambda…”);

};

// JDK8之前使用匿名类来实现

Runnable b = new Runnable() {

@Override

public void run() {

System.out.println(“Hello World,Lambda…”);

}

};

startThread(a);

startThread(() -> {

System.out.println(“Hello World,Lambda…”);

});

}

那能将(int a) -> {System.out.println(“Hello World, Lambda…”);}表达式赋值给Runnable a变量吗?答案是不能,因为该表达式不符合函数式编程接口(Runnable)唯一抽象方法的函数签名列表。

Runnable的函数式签名列表为public abstract void run();

温馨提示:如果我们有留意JDK8的Runnable接口的定义,你会发现给接口相对JDK8之前的版本多了一个注解:@FunctionalInterface,该注解是一个标识注解,用来标识这个接口是一个函数式接口。如果我们人为在一个不满足函数式定义的接口上增加@FunctionalInterface,则会在编译时提示错误。

3、 Lambda表达式实战思考

例如有如下代码:

/**

  • 处理文件:当前需求是处理文件的第一行数据

  • @return

  • @throws IOException

*/

public static String processFile() throws IOException {

try(BufferedReader br = new BufferedReader(new FileReader(“data.txt”))) {

return br.readLine();

}

}

当前需求为处理文件的第一行数据,那问题来了,如果需求变化需要返回文件的第一行和第二行数据,那该如何进行改造呢?

在理想的情况下,需要重用执行设置和关闭流的代码,并告诉processFile()方法对文件执行不同的操作,换句话说就是要实现对processFile的行为进行参数化。

Step·1:行为参数化

要读取文件的头两行,用Lambda语法如何实现呢?思考一下,下面这条语句是否可以实现?

(BufferedReader bf) -> br.readLine() + br.readLine()

答案是当然可以,接下来就要思考,定义一个什么样的方法,能接收上面这个参数。

Step2:使用函数式接口来传递行为

要使用(bufferedReader bf) -> br.readLine() + br.readLine(),则需要定义一个接受参数为BufferedReader,并返回String类型的函数式接口。

定义如下:

@FunctionalInterface

public interface BufferedReaderProcessor {

public String process(BufferedReader b) throws IoException;

}

那把processFile方法改造成如下代码:

/**

  • 处理文件:当前需求是处理文件的第一行数据

  • @return

  • @throws IOException

*/

public static String processFile(BufferedReaderProcess brp) throws IOException {

try(BufferedReader br = new BufferedReader(new FileReader(“data.txt”))) {

return brp.process(br);

}

}

Step3:使用lambda表达式作为参数进行传递

将行为参数化后,并对方法进行改造,使方法接受一个函数式编程接口后,就可以将Lambda表达式直接传递给方法,例如:

processFile( (BufferedReader br) -> br.readLine() );

processFile( (BufferedReader bf) -> br.readLine() + br.readLine());

4、Java8中自定义函数式接口

从上面的讲解中我们已然能够得知,要能够将Lambda表达式当成方法参数进行参数行为化的一个前提条件是首先要在方法列表中使用一个函数式接口,例如上例中的BufferReaderProcess,那如果每次使用Labmbda表达式之前都要定义各自的函数式编程接口,那也够麻烦的,那有没有一种方式,或定义一种通用的函数式编程接口呢?答案是肯定的,Java8的设计者,利用泛型,定义了一整套函数式编程接口,下面将介绍java8中常用的函数式编程接口。

4.1 Predicate

在这里插入图片描述

所谓函数式编程接口就是只能定义一个抽象方法,Predicate函数接口中定义的抽象方法为boolean test(T t),对应的函数式行为为接收一类对象t,返回boolean类型,其可用的lambda表达式为(T t) -> boolean类型的表达式,例如(Sample a) -> a.isEmpty()。

该接口通常的应用场景为过滤。例如,要定义一个方法,从集合中进行刷选,具体的刷选逻辑(行为)由参数进行指定,那我们可以定义这样一个刷选的方法:

public static List filter(List list, Predicate p) {

List results = new ArrayList<>();

for(T s: list){

if(p.test(s)){

results.add(s);

}

}

return results;

}

上述函数,我们可以这样进行调用:

Predicate behaviorFilter = (String s) -> !s.isEmpty(); // lambda表达式赋值给一个变量

filter(behaviorFilter);

其它add等方法,将在下文介绍(复合lambda表达式)。

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:IntPredicate、LongPredicate、DoublePredicate。我们选择LongPredicate看一下其函数接口的声明:

boolean test(long value);

4.2 Consumer

在这里插入图片描述

该函数式编程接口适合对对象进行处理,但没有返回值,对应的函数描述符:T -> void

举例如下:

public static void forEach(List list, Consumer c) {

for(T t : list) {

c.accept(t);

}

}

其调用示例如下:

forEach( Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i) );

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:IntConsumer、LongConsumer、DoubleConsumer。

4.3 Function<T,R>

在这里插入图片描述

其适合的场景是,接收一个泛型T的对象,返回一个泛型为R的对象,其对应的函数描述符: T -> R。

示例如下:

public static <T,R> List map(List list, Function<T,R> f) {

List result = new ArrayList<>();

for(T t : list) {

result.add( f.apply(t) );

}

return result;

}

List l = map(Arrays.asList(“lambdas”, “in”, “action”), (String s) -> s.length );

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:IntFunction< R>、LongFunction< R>、DoubleFunction< R>、IntToDoubleFunction、IntToLongFunction、LongToIntFunction、LongToDoubleFunction、ToIntFunction< T>、ToDoubleFunction< T>、ToLongFunction< T>。

4.4 Supplier< T>

在这里插入图片描述

函数描述符:() -> T。适合创建对象的场景,例如 () -> new Object();

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:BooleanSupplier、IntSupplier、LongSupplier、DoubleSupplier。

4.5 UnaryOperator< T >

在这里插入图片描述

一元运算符函数式接口,接收一个泛型T的对象,同样返回一个泛型T的对象。

示例如下:

public static List map(List list, UnaryOperator f) {

List result = new ArrayList<>();

for(T t : list) {

result.add( f.apply(t) );

}

return result;

}

map( list, (int i) -> i ++ );

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:IntUnaryOperator、LongUnaryOperator、DoubleUnaryOperator。

4.6 BiPredicate<T,U>

在这里插入图片描述

接收两个参数,返回boolean类型。其对应的函数描述符:(T,U) -> boolean。

4.7 BiConsumer<T,U>

在这里插入图片描述

与Consume函数式接口类似,只是该接口接收两个参数,对应的函数描述符(T,U) -> void。

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:ObjIntConsumer、ObjLongConsumer、ObjDoubleConsumer。

4.8 BiFunction<T,U,R>

在这里插入图片描述

与Function函数式接口类似,其对应的函数描述符:(T,U) -> R。

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:ToIntBiFunction(T,U)、ToLongBiFunction(T,U)、ToDoubleBiFunction(T,U)。

4.9 BinaryOperator< T >

在这里插入图片描述

二维运算符,接收两个T类型的对象,返回一个T类型的对象。

另外,为了避免java基本类型与包装类型的装箱与拆箱带来的性能损耗,JDK8的设计者们提供了如下函数式编程接口:IntBinaryOperator、LongBinaryOperator、DoubleBinaryOperator。

上述就是JDK8定义在java.util.function中的函数式编程接口。重点关注的是其定义的函数式编程接口,其复合操作相关的API将在下文中详细介绍。

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

5.1 类型检查

java8是如何检查传入的Lambda表示式是否符合约定的类型呢?

例如

public static List filter(List list, Predicate p) {

List results = new ArrayList<>();

for(T s: list){

if(p.test(s)){

results.add(s);

}

}

return results;

}

List heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);

其类型检测的步骤:

1)首先查看filter函数的参数列表,得出Lambda对应的参数类型为Predicate。

2)函数式接口Predicate中定义的抽象接口为 boolean test(T t),对应的函数描述符( T -> boolean)。

3)验证Lambda表达式是否符合函数描述符。

总结

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。

这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来

目录:

部分内容截图:

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

总结

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。

这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来

目录:

[外链图片转存中…(img-McwA42mZ-1713610612023)]

部分内容截图:

[外链图片转存中…(img-q0TMQsWn-1713610612023)]

[外链图片转存中…(img-YwPaIRTR-1713610612024)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-lQTP5PQx-1713610612024)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值