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到底是啥
看到上面的@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);
关于其他的双冒号的几种情况(对象形式,静态方法形式,构造器形式)使用这里就不一一列举了。个人理解的双冒号的大致使用如此,如有错误还请指出谢谢