目录
1.什么是Lambda表达式
- Lambda表达式是JDK8的一个新特性,可以取代接口的匿名内部类,能让你写出更加优雅的代码 。
- 它可以写出更加简洁,更加灵活的代码,作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升 。
- 他是一种高端的玩法,人上人的玩法
2.使用前提
- 并不是所有的接口都能使用Lambda表达式创建子类对象,只有功能接口才能使用。
- Lambda表达式要求的接口中,必须有且仅有一个必须要实现的抽象方法。这种接口在Java中,被称之为"功能接口" 。
- 功能接口语法上使用注解@FunctionalInterface标记在接口头上用来检测一个接口是不是功能接口 。
就比如这样,加上@FunctionalInterface不报错他就是功能接口
@FunctionalInterface
interface I{
void test();
}
那么思考两个问题 :
1.功能接口中只能有一个方法吗?
答案肯定是不是,Java8中的默认方法和静态方法不需要子类实现,功能接口中可以允许有它们存在。
// 就比如这样它依然是功能接口,并不会报错
@FunctionalInterface
interface IA{
void test();
default void test2(){};
static void test3(){};
}
2.功能接口中只能有一个抽象方法吗?
不是,有极个别比较特殊的抽象方法,可以不需要子类实现:就比如
Object类是Java每一个类的父类,所以Object类当中的方法实现就可以作为接口抽象方法的实现。
// 重写了equals和hashcode方法它依然是功能接口 因为他们不是必须要实现的抽象方法
@FunctionalInterface
interface IB{
void test();
boolean equals(Object obj);
int hashCode();
}
再强调一下,功能接口指的是有且仅有一个必须要子类实现的抽象方法的接口
3.基本使用
3.1.语法:()->{}
(形参列表) -> {
// 方法体
}
解释一下:
(形参列表)
表示功能接口中,必须要重写的抽象方法的形参列表。->
由一个英文横杠 + 英文大于号
字符组成,它是Lambda表达式的运算符,读作goes to
。{ //方法体 }
表示功能接口中,必须要重写的抽象方法的,方法体实现。
3.2.具体使用(这里举了几个简单的接口例子)
- 无参无返回值的
// 接口
@FunctionalInterface
interface IC{
void test();
}
// Lambda实现
IC ic = ()->{ System.out.println("起飞666"); };
ic.test();
- 需要一个参数但无返回值
// 接口
@FunctionalInterface
interface ID{
void test(int a);
// Lambda实现
ID id = (int a) -> {System.out.println("我输出了" + a);};
id.test(18);
- 数据类型可以省略,编译器会自动进行类型推断
// 接口
@FunctionalInterface
interface ID{
void test(int a);
// Lambda实现
ID id = (a) -> {System.out.println("我输出了" + a);};
id.test(18);
- lambda若只需要一个参数时,参数的小括号可以省略
// 接口
@FunctionalInterface
interface ID{
void test(int a);,
// Lambda实现
ID id = a -> {System.out.println("我输出了" + a);};
id.test(18);
- 当重写方法的方法体的语句仅有一条语句时,那么可以省略大括号,特殊的,当这一条语句就是方法的返回值语句时,那么{}和return可以一起省略。
// 接口
interface IE{
int test(int a,int b);
// Lambda实现
IE ie = (a, b) -> a+b;
System.out.println(ie.test(129,391));
3.3.方法引用
实际上在多数情况下,都不太可能一句话把方法体写完。多数情况下,Lambda表达式的抽象方法实现都会很复杂,那这样Lambda表达式就会写的很复杂,这就很难受了。而Lambda表达式,本质上就是重写了一个抽象方法的子类对象,所以Java允许Lambda表达式的抽象方法的实现可以直接指向一个已经存在的方法,而不是自己书写实现。这种语法在Java中称之为"方法引用"!
3.3.1.方法引用的条件
- 访问权限修饰符和static之类的修饰符,实际上没有多大影响,但是肯定需要访问权限。
- 返回值类型应该保持一致(如果抽象方法返回一个父类引用类型、那么这个已实现的方法可以返回子类类型)。
- 方法名是什么不重要。
- 形参列表必须保持一致、数据类型、位置必须严格对应,但是形参名无所谓。
- 方法体无所谓,自己重写即可。
3.3.2.方法引用的语法
- 不去掉运算符的格式:(形参列表) -> 已实现的方法(形参列表);
- 如果这个方法是静态方法,用类名.调用
- 如果是个成员方法,用对象名.调用
- 上述语法中的形参列表必须保持一致,表示将抽象方法中形参传递给已实现的方法
// 重写的接口
/*
*功能接口
* 无参无返回值
* */
@FunctionalInterface
interface IC{
void test();
}
/*
* 需要一个参数但无返回值
* */
@FunctionalInterface
interface ID{
void test(int a);
}
/*
*int 返回值 两个参数
*/
@FunctionalInterface
interface IE{
int test(int a,int b);
// 方法的引用
public void test1(){
System.out.println(666);
System.out.println(666);
System.out.println(666);
}
public void test2(int a){
System.out.println(777);
System.out.println(777);
System.out.println(777);
System.out.println(a);
}
public static int test3(int a,int b){
System.out.println(888);
System.out.println(888);
System.out.println(888);
return a+b;
}
// Lambda 调用
IC ic1 = ()-> new MyTest().test1();
ic1.test();
ID id1 = a -> new MyTest().test2(a);
id1.test(9420);
IE ie1 = (a,b) -> MyTest.test3(a,b);
System.out.println(ie1.test(100, 200));
- 功能接口 引用名 = 方法的归属者::方法名
// 还是上面的三个接口 用第二种方法实现
IC ic2 = new MyTest()::test1;
ic2.test();
ID id2 = new MyTest()::test2;
id2.test(99);
IE ie2 = MyTest::test3;
System.out.println(ie2.test(99,999));