一、引言
lambda涉及到了函数式编程,是函数式编程的一部分,有这个概念就行了。接下来是使用lambda的对比:
List<String> str1 = new ArrayList<>();
str1.add("hello");
str1.add("world");
//下面就是lambda表达式之“::”的应用了
str1.forEach(System.out::println);
//等效于
str1.forEach((item)->{
System.out.println(item);
});
双冒号时固定写法:(类::方法); 左边的System.out含有的PrintStream类,右边为println方法,println方法签名和foreach方法的参数函数式接口(@FunctionalInterface
)签名一致,就可以省略方法名后面的固定格式,本质是省略代码长度。
- 一个接口只有一个抽象方法,就可被写为lambda表达式来实现,本质就是一个匿名内部类
- 简化步骤:实现类=〉静态内部类=〉局部内部类=〉匿名内部类=〉Lambda表达式
- 可以加上
@FunctionalInterface
,来进一步标识。自己实现时,可有可无。
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
二、所以该冒号总共有三种使用方式:
- 类::静态方法
- 实例::方法
- 类::实例方法
- 类::new
- 类/原始类型[]::new
对应来说:
- 类::静态方法
–工具类的方法经常被用来使用,该方法是static的
–对应的Lambda表达式为(a,b…)->类名.类方法(a,b…)
Utils::generateRandom;
Utils.generateRandom(); //等于这种写法
- 实例::方法
–对应的Lambda表达式为(a,b…)->特定对象.实例方法(a,b…)
Animal cat = new Cat();
cat::eat; //前面是实例对象,后面的方法可以不是static
new Cat()::eat; //当然也可以这样写
// 按Ctrl+Alt+V,可推出前面[idea],可以看到返回的是一个 @FunctionalInterface 功能性接口
// 因为我写的返回值是void所以是Runnable,可以自行试试其他的
Runnable speak = new Test3()::speak;
- 类::实例方法
–上面我们知道返回的其实是定义的功能性接口,那么接下来这种,我们就自己定义一个接口
–通常我们准备用的方法不是静态类型的,而是传一个对象过去执行对应参数
–可以传入[定义类]Test2或者[定义类的父类]Test1传入进去,向上兼容;
–对应的Lambda表达式为(a,b…)->a.实例方法(b…)
公式:类::实例方法 Test2::speak 等于 (t, p1, p2) -> t.speak(p1, p2)
public class Test1 {
public void a(Integer param1,int param2){
System.out.println("Test1 = " + param1 + param2);
}
public static void main(String[] args) {
// 可以传入[定义的类][Test2]或者[定义的类的父类][Test1]传入进去,向上兼容;
MyInter m1 = Test1::a;
// 注意!!!这里的Test1只用于引用a方法,实际使用时,还是传Test2
Test2 test2 = new Test2();
m1.d(test2, 1,3); // Test1 = 13
MyInter m2 = (test2, p1, p2) -> test2.speak(p1, p2);//默认是Test2
// 等于:MyInter m2 = Test2::speak;
m2.d(test2, 1,3); // Test2 = 4
}
}
class Test2 extends Test1 {
void speak(Integer param1,int param2){
System.out.println("Test2 = " + (param1 + param2));
}
}
@FunctionalInterface
interface MyInter {
// 接口参数比上述的a方法参数数量多一个,即被调用的类[第一个],其它参数要一致
// 且Test1::a的Test1是该入参类型Test2的父类,向少的类扩展
public void d(Test2 d,int param1,int param2);
}
-
引用构造器:类名::new
–对应的Lambda表达式为(a,b…)->new 类名(a,b…)
–这个时候 接口 返回值可有T a(a,b…),可无void b(a,b…)都能匹配。 -
类/原始类型[]::new
–对应的Lambda表达式为(x)->new 类名[x]
附:(自定义输出)
public class TestI {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.forEach(TestI::addSomething);
}
static void addSomething(String s){
System.out.println(s+" Doge!");
}
}
遍历map
map.entrySet().forEach(entry ->
System.out.println(entry.getKey() + entry.getValue()));
map.entrySet().iterator().forEachRemaining(item ->
System.out.println(item.getKey()+item.getValue()));
// forEachRemaining是Iterator的方法,forEach是Iterable的接口中的方法。
// forEachRemaining本质是 while(iterator.hasNext()){iterator.next()} 操作,调用了hasNext所以cursor最后超出,对于一个Iterator对象来说这是不可逆的。
// forEach本质是 for (Type e : collection) {...} 操作
map.forEach( (k, v) -> {
System.out.println("key:value = " + k + ":" + v);
});