什么是Lambda表达式?写一篇Lambda表达式的总结
在Java中,当一个接口只有一个抽象方法时,则将其称之为函数式接口,为其提供一个Lambda表达式会让编译器为其生成一个实例对象,这个Lambda表达式中的代码块能覆盖这个实例对象的方法,并在以后执行。
什么是Lambda表达式,举例说明:
//这里有一个Dog接口
public interface Dog {
void eat(String msg);
}
这里定义了一个Dog接口,由于这个接口中只有一个抽象方法,所以它是一个函数式接口,根据以往的经验如果想要使用这个接口中的eat方法,首先要创建一个实现了Dog接口的对象并重写eat方法。
Dog dog1 = new Dog() {
@Override
public void eat(String msg) {
System.out.println(msg);
}
};
dog1.eat("第一条小狗正在吃饭");
运行结果:第一条小狗正在吃饭
现在用Lambda表达式来代替上面这段代码:
//这是一个lambda表达式:(String msg) -> { System.out.println(msg);}
Dog dog2 = (String msg) -> { System.out.println(msg);};
dog2.eat("第二条小狗正在吃饭");
运行结果:第二条小狗正在吃饭
使用Lambda表达式只需要两行代码就能实现相同的功能,并且通过上面这段代码可以得到Lambda表达式的语法为:
(params) -> { method body }
箭头左侧小括号内表示方法的类型及方法名,箭头右侧则为一个方法体,方法体中可以编写具体的代码。
方法引用:
在日常开发的过程中,是否经常见到使用以下这种方式打印某个字符串
String[] msg = "Hello world".split("");
Arrays.stream(msg).forEach(System.out::println);
这个表达式是一个方法引用System.out::println
,System.out是PrintStream对象。这个表达式等价于(x) -> {System.out.println(x);}
,我认为可以把方法引用理解为Lambda的一种简写形式。与Lambda表达式一样,它会使编译器为函数式接口创建一个实例,并在实例中将抽象方法重写为给定的方法,在这里,System.out::println
就是给定的方法。
需要注意的是,不管是Lambda表达式还是方法引用,本身都不是一个对象。只是当他们被传递到函数式接口时,这个接口就会被创建一个实例对象。
使用方法引用的几种形式:
- 实例方法引用
- 对象::实例方法 == System.out::println == (x) -> { System.out.println(x) }
- 类名::实例方法 == String::toLowerCase == (x) -> { x.toLowerCase() }
- 静态方法引用
- 类名::静态方法 == Integer::sum == (x,y) -> { Integer::sum(x,y) }
对象::实例方法,示例代码:
//这里有一个Dog接口
public interface Dog {
//吃东西
void eat(String msg);
}
public class Test {
public static void main(String[] args) {
Dog dog = System.out::println;
dog.eat("小狗吃东西了");
}
}
运行结果:小狗吃东西了
类名::实例方法,示例代码:
//这里有一个Dog接口
public interface Dog {
//叫两声
String say(String msg);
}
public class Test {
public static void main(String[] args) {
Dog dog = String::toLowerCase;
String msg = dog.say("Wang Wang Wang !!!");
System.out.println(msg);
}
}
运行结果:wang wang wang !!!
类名::静态方法,示例代码:
//这里有一个Dog接口
public interface Dog {
//计算有几只小狗
int count(int bdognum,int wdognum);
}
public class Test {
public static void main(String[] args) {
Dog dog = Integer::sum;
int count = dog.count(6,8);
System.out.println(count);
}
}
运行结果:14
构造器引用
示例代码:
//这里有一个Dog接口
public interface Dog {
//狗想要一个主人
void withPeople(String m);
}
//这有个人
public class People {
//人类构造器
public People(String d){
System.out.println(d);
}
}
public class Test {
public static void main(String[] args) {
Dog d = People::new;
d.withPeople("这个狗得到了一个主人");
}
}
运行结果:这个狗得到了一个主人
其中,People::new
就是一个构造器引用,Dog d = People::new;
所做的操作实际上就是将函数式接口中的方法内容重写为new People()
,由于构造方法中的参数是String类型,所以会自动调用public People(String d)
构造器。当程序执行withPeople
方法的时候就会去构建一个People实例。
关于Lambda表达式中变量的注意事项
下面这段代码会报错:
public class Test {
public static void main(String[] args) {
String a ="哈哈";
Dog d = (x) -> {
//注意,这一行会报错
a = a+"";
System.out.println(x);
};
d.say("wang");
}
}
在idea编辑器中,a = a+"";
会提示错误信息。原因是在Lambda中,不允许修改外围作用域中的变量值,因为如果可以在Lambda表达式中修改变量,多线程环境下就会不安全。
再看一个会报错的示例:
public class Test {
public static void main(String[] args) {
String a ="哈哈";
a = a+"";
Dog d = (x) -> {
//这一行会提示错误信息,因为 a 在外部被修改了
System.out.println(a);
};
d.say("wang");
}
}
System.out.println(a);
会报错,因为a在外部被修改了。由此可以判断,如果在表达式中引用了一个变量,那么这个变量即使是在表达式外部被改变也是不合法的。
其他注意事项:不能在lambda表达式中声明与外部变量同名的变量
转载自:https://juejin.cn/post/7229776208976003133