Java的lambda表达式以及双冒号写法的一些浅薄理解

本文详细讲解了lambda表达式如何通过函数式接口实现在Java中的使用,展示了其与传统方法参数传递的区别,以及`Consumer`接口在`forEach`方法中的应用,包括双冒号::的特殊用法和示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

lambda表达式和函数式接口

首先lambda是想将函数当成参数传递给某个方法,设计者是想做这样的设计,
但是实际上java是不能直接在方法参数里塞方法的,只能够塞对象。那咋办呢?
于是就引入了lambda表达式的设计,这种设计通过简化函数式接口的书写,实现了看起来像是真的将函数当成参数传递给某个方法了。但是实际上还是仅仅传入了函数式接口作为参数,不是真的把方法塞到方法参数里去。

看以下代码,这里结合List的forEach方法。首先先匿名实现了一个函数式接口caseitemFunction (见第一步),
由于是函数式接口,所以可以使用lambda表达式实现:(见第二步) 。两者是等价的。
// 第一步,匿名实现了一个函数式接口,创建了一个匿名的该接口的实现类对象
Consumer<CaseitemProcess> caseitemFunction = new Consumer<CaseitemProcess>() {
	@Override
	public void accept(CaseitemProcess caseitem) {
		caseitem.setId(caseitem.getId()+"111");
	}
};

// 第二步,使用lambda表达式实现函数式接口,创建了一个对象
Consumer<CaseitemProcess> lambdaFunction = v->v.setId(v.getId()+"111");;
caseitemProcesses.forEach(caseitemFunction);
caseitemProcesses.forEach(lambdaFunction );

// 第三步,forEach方法里塞的实际上是第二步同种对象,
// 这样写看起来就像是传入了方法一样
caseitemProcesses.forEach( v -> v.setId(v.getId()+"111") );
然后看第三步,此时就可以将第二步里的使用lambda方式,直接写入forEach()方法里,
实际上  v->v.setId(v.getId()+"111");  不是一个方法,而是一个对象。

我们再跳入list的forEach方法里验证下,看看里面参数到底是啥
请添加图片描述
我们发现这个传入的是一个Comsumer对象,然后我们再看看这个Comsumer到底是啥
Comsumer接口的源码
看到上面的@FunctionalInterface了么?这个注解是注明函数式接口的,也就是说forEach方法传入的就是一个函数式接口。

我们知道传入的参数实际上是匿名实现的函数式接口(注意接口本身不能被实例化),那么这个Consumer是怎么执行lambda传进去的方法的呢?答案是直接调用他的唯一方法accept()即可

Consumer<CaseitemProcess> caseitemFunction = new Consumer<CaseitemProcess>() {
	@Override
	public void accept(CaseitemProcess caseitem) {
		caseitem.setId(caseitem.getId()+"111");
	}
};

CaseitemProcess process = new CaseitemProcess();
process.setId(222);
// 此时就执行里面将id设成+111的方法了
caseitemFunction.accept(process); 

forEach()方法是怎么遍历并使用函数式接口的

上面代码第一步和第二步,实际上是一样的,都是匿名实现了一个接口。两者是一模一样的,然后把这个对象给传到forEach方法里。
然后看下面集合的 forEach() 方法,我们看到他的方法里,用for(T t : this)遍历自身元素,
(由此看到同是称为forEach,但是for(A a:list) 这种方式 是比list.forEach()更加接近底层的 )

由于list本身是继承于Collection,而Collection本身是继承Iterator的。所以这个this指的就是list集合本身
每一次遍历,都会调用函数式接口的accept()方法。

(看上面代码的第一步,此时每一次遍历都会调用 caseitemFunction.accept(Caseitem item) 方法,即调用
caseitem.setId(caseitem.getId()+“111”); )
在这里插入图片描述

双冒号::的基本使用

那么双冒号实际上是特殊的lambda表达式,上文第二步我们使用的lambda表达式是这样


Consumer<CaseitemProcess> lambdaFunction = v->v.setId(v.getId()+"111");;

假如某个函数式接口不需要做 将id再加上"111"的特殊操作,而仅仅是只需要获得id而已——仅仅是用到类本身自带的方法而已。
此时的lambda表达式就变成了这样

Consumer<CaseitemProcess> lambdaFunction = v->v.getId();

此时由于使用了类自带的方法,就可以使用双冒号写法

Consumer<CaseitemProcess> maohaoFunction = CaseitemProcess::getId;

放入forEach中进行比较,两者等价

list.forEach( v -> v.getId());
list.forEach( CaseitemProcess::getId);

关于其他的双冒号的几种情况(对象形式,静态方法形式,构造器形式)使用这里就不一一列举了。个人理解的双冒号的大致使用如此,如有错误还请指出谢谢

### Java Lambda表达式冒号的作用 在Java Lambda表达式中,箭头`->`前后的部分分别表示参数列表和主体。具体来说: - 参数列表位于箭头左侧,可以为空、单个参数或多个参数。当只有一个参数时,圆括号可省略;对于无参的情况,则需保留空的圆括号。 - 主体位于箭头右侧,如果只有一条语句则大括号可以省去,多条语句需要用大括号包裹起来形成代码块。 例如下面这段代码展示了如何定义并使用带有单一参数的Lambda表达式[^1]: ```java Consumer<CaseitemProcess> lambdaFunction = v -> v.setId(v.getId() + "111"); ``` 这里`v`代表传入的对象实例,而`v.setId(...)`则是对该对象执行的操作。通过这种方式编写的匿名内部类不仅更为简洁直观,而且提高了代码的可读性和维护效率[^2]。 另外,在某些情况下还可以利用双冒号操作符来简化书写形式。该符号允许开发者引用现有方法作为实现逻辑的一部分,从而进一步减少冗余代码量。需要注意的是,此时并不立即调用所指定的方法,而是将其封装成函数接口的形式供后续调用[^3]。 #### 双冒号的不同应用场景如下所示[^4]: - **静态方法引用** ```java Comparator<String> comparator = String::compare; ``` - **特定对象上的实例方法引用** ```java Consumer<String> printer = System.out::println; ``` - **任意类型实例上的实例方法引用** ```java BiPredicate<List<String>, String> contains = List::contains; ``` - **构造器引用** ```java Supplier<ArrayList<Integer>> supplier = ArrayList::new; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值