1、Lambda表达式的概念
- Lambda表达式时Java8中值得学习的新特性之一。
- Lambda表达式从本质上来说是一个匿名方法,使用这个匿名方法来实现接口中的方法,方便简洁,简化代码。
2、Lambda表达式的使用场景
Lambda表达式最大的优点就是实现接口时大大的简化了代码。
//创建一个接口
package cn;
public interface Human {
public int test(int a);
}
//================3种实现方法==================================
package cn.Impl;
import cn.Human;
public class Program {
public static void main(String[] args){
//1、显示实现类对象
Human student1 = new Student();
}
private static class Student implements Human{
@Override
public int test(int a) {
return a*a;
}
}
//2、使用匿名内部类实现
Human student2 = new Human() {
@Override
public int test(int a) {
return a*a;
}
};
//3、使用Lambda表达式实现
Human student3 = a -> a * a;
}
3、Lambda表达式对接口的要求
- 并不是所有的接口都可以使用Lambda表达式,毕竟Lambda表达式本质上来说只是一个匿名方法,只有当接口当中必须要实现的方法有且仅有一个,才能用Lambda表达式实现。
- Lambda表达式只能用来实现函数式接口。
4、函数式接口
4.1 基础概念
定义 :
- 在一个接口中,要求实现类必须实现的方法,有且仅有一个,这样的接口,称为函数式接口 。
//Test1接口中只有一个需要被实现的方法,所以Test1是一个函数式接口
interface Test1{
void test1();
}
//Test2接口中有大于1的方法需要被现实,所以Test2不是一个函数式接口
interface Test2{
void test2();
void test3();
}
//Test3接口中没有方法需要被实现,所以Test3不是一个函数式接口
interface Test3{
}
//Test4接口虽然没有定义方法,但是可以从父接口Test1中继承一个抽象方法,所以Test4也是一个函数式接口
interface Test4 extends Test1{
}
//Test5中虽然有两个方法,但是default修饰的方法不是子类必须要实现的,所以必须要实现的方法只有一个,Test5也是个函数式接口
interface Test5{
void test5();
default void test();
}
//Test6接口中的toString方法时Object类中定义的
//在实现类实现接口中的方法的时候,toString可以不重写!因为可以从父类Object中继承到
//此时,Test6接口中必须实现的方法只有一个,所以Test6也是一个函数式接口
interface Test6{
void test6();
String toString();
}
4.2 @FunctionalInterface
- 此注解用在接口之前,,可以判断接口是否是 一个函数式接口。
- 如果是一个函数式接口,没有任何问题,如果不是一个函数式接口,则会报错。功能类似于
@Override
@FunctionalInterface
interface FunctionalInterface{
void test();
}
5、Lambda表达式的基础语法
- Lambda表达式本质上是一个匿名函数,因此我们写Lambda表达式的时候不需要关心方法名和返回值类型,只需关注两个地方即可:参数列表 和 方法体
基础语法:
(参数) -> {
方法体
}
- 参数列表:要求和实现的接口中的方法参数一致,包括数量和类型
- 方法体:如果接口定义有返回值,那么在实现的时候,注意返回值的返回。
->
:分隔参数部分和方法体部分
//基础语法示例:
//定义多个函数式接口
//1、无参数无返回值
public interface NoneReturnNoneParamter {
void test();
}
//2、有一个参数无返回值
public interface NoneReturnSingleParamter {
void test(int a);
}
//3、有多个参数无返回值
public interface NoneReturnMultipleParamter {
void test(int a, int b,int c);
}
//4、无参数有返回值
public interface SingleReturnNoneParamter {
int test();
}
//5、有一个参数有返回值
public interface SingleReturnSingleParamter {
int test(int a);
}
//6、有多个参数有返回值
public interface SingleReturnMultipleParamter {
int test(int a,int b,int c);
}
//=======================Lambda表达式实现==================================
public class BasicSyntax {
public static void main(String[] args){
//1、实现无参无返回值的函数式接口方法
NoneReturnNoneParamter Lambda1 = () -> {
System.out.println("这是一个无参无返回值的方法");
};
Lambda1.test();
//2、实现有一个参数无返回值的函数式接口方法
NoneReturnSingleParamter Lambda2 = (int a) -> {
System.out.println("这是一个只有一个参数无返回值的方法,参数a的值=" + a);
};
Lambda2.test(6);
//3、实现多个参数无返回值的函数式接口方法
NoneReturnMultipleParamter Lambda3 = (int a, int b, int c) ->{
System.out.println("这是一个多个参数无返回值的方法,参数abc的值分别为:"+ a + "," + b + "," + c);
};
Lambda3.test(10,20,30);
//4、实现无参数有返回值的函数式接口方法
SingleReturnNoneParamter Lambda4 = () -> {
int a = 2;
return a;
};
System.out.println("这是一个无参数有返回值的方法,返回值为:"+ Lambda4.test());
//5、实现有一个参数有返回值的函数式接口方法
SingleReturnSingleParamter Lambda5 = (int a) -> {
return a*a;
};
System.out.println("这是有一个参数有返回值的方法,参数的平方为:" + Lambda5.test(6));
//6、实现有多个参数有返回值的函数式接口方法
SingleReturnMultipleParamter Lambda6 = (int a,int b,int c) ->{
return a*b*c;
};
System.out.println("这是有多个参数有返回值的方法,参数相乘结果为:" + Lambda6.test(10,20,30));
}
}
6、Lambda表达式语法进阶
- 上述代码中,确实使用了Lambda表达式实现接口,但是还可以再精简一些
6.1 参数部分省略
- 由于在接口的方法中,已经定义了参数的类型,而且在使用Lambda表达式实现接口的时候,必须保证参数数量和参数类型与接口中的方法保持一致,因此在使用Lambda表达式的时候参数的类型可以不用写。
- 需要注意的是,参数类型如果需要省略,要么全部省略,要么全部不要省略,不要出现有的省略有的不省略的情况。
- 如果参数列表的参数仅且只有一个,小括号可以省略
//参数类型可以不用写
SingleReturnSingleParamter Lambda5 = (a) -> {a
return a*a;
};
//如果参数列表的参数仅且只有一个,小括号可以省略
SingleReturnSingleParamter Lambda5 = a -> {a
return a*a;
};
6.2 方法体部分省略
- 如果说方法体中的代码有且只有一句,那么方法体的大括号可以省略
- 在方法体中的代码有且只有一句的前提下,如果方法有返回值,那么
return
也要一起省略
SingleReturnSingleParamter Lambda5 = a -> {
return a*a;
};
//省略为以下
SingleReturnSingleParamter Lambda5 = a -> a*a;
7、函数引用
- Lambda表达式的诞生是为了简化接口实现,所以在其表达式中是不应该出现比较复杂的逻辑的,不然会对程序造成很大的影响,如果处理的逻辑比较复杂,一般会单独写一个方法实现,然后在Lambda表达式中引用。
- 引用一个已经存在的方法,使其代替Lambda表达式完成接口的实现
7.1 静态方法的引用
语法:
- 类::静态方法名
注意事项:
- 在引用的方法后面,不要加小括号
- 引用的这个方法,参数的类型和数量、返回值,必须和接口定义的一致
package cn.Impl;
public class Test {
private interface Calculate{
int calculate(int a,int b);
}
public static void main(String[] args) {
//Calculate calculate = (a,b) -> calculate(a,b);
//静态函数引用 类::静态方法名 (后面不用加括号)
Calculate calculate = Test::calculate;
System.out.println(calculate.calculate(20,20));
}
//以下方法的参数类型数量和返回值必须和接口定义的方法保持一致
private static int calculate(int x,int y){
if (x > y) return x-y;
else if (x < y) return y-x;
return x+y;
}
}
7.2 非静态方法的引用
语法:
- new 对象::非静态方法名
注意事项:
- 非静态方法对象需要new出来
- 在引用的方法后面,不要加小括号
- 引用的这个方法,参数的类型和数量、返回值,必须和接口定义的一致
package cn.Impl;
public class Test {
private interface Calculate{
int calculate(int a,int b);
}
public static void main(String[] args) {
//引用一个非静态方法
Calculate calculate = new Test()::calculate2;
System.out.println(calculate.calculate(10,20));
}
private int calculate2(int x,int y){
if (x > y) return x*x;
else if (x < y) return y*y;
return x+y;
}
}
7.3 构造方法的引用
语法:
- 类名::new
注意事项:
- 可以通过接口中的方法参数,区分引用不同的构造方法
- 被引用构造方法的类中一定要有对应参数的构造方法才可以被引用,否则将会报错
8、对象方法的特殊引用
- 如果使用Lambda表达式实现接口的时候,接口方法参数中包含了某一个对象,Lambda表达式中可以直接使用这个对象并调用其中的方法来实现整体逻辑。
public class Person{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
//参数中有对象
interface Inf{
//String get(Person person);
String set(Person person,String name);
}
public class Test{
public static void main(String[] args){
Person person = new Person();
person.setName("小明");
//直接调用
//Inf inf = Person::getName;
Inf inf = Person::setName;
//System.out.println(inf.get(person));
inf.set(person,"小红");
System.out.println(person.getName());
}
}
9、Lambda表达式注意事项
- 一个局部变量被Lambda表达式引用后,会默认修改为final类型,变量值将无法改变