Lambda表达式简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更简洁的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
1.函数式接口
基础概念:
如果一个接口中,要求实现类必须实现的抽象方法,有且只有一个!!!这样的接口就是函数式接口。
可以使用@FunctionalInterface注解来确定该接口是不是函数式接口,不是函数式接口使用这个注解会报错。
public class FunctionalInterface{
//这个接口只有一个方法并且是必须实现的,是函数式接口
@FunctionalInterface
interface Test1{
void test();
}
//这个接口有两个必须实现的方法!!!不是函数式接口
@FunctionalInterface
interface Test2{
void test1();
void test2();
}
//这个接口有0个必须实现的方法!!!不是函数式接口
@FunctionalInterface
interface Test3{ }
//这个接口没有必须实现的方法,但是通过继承Test1接口,得到一个父接口的抽象方法,所以是函数式接口
@FunctionalInterface
interface Test4 extends Test1{}
//这个接口有两个方法,default方法子类并不是必须实现的,除此之外有一个必须实现的抽象方法,所以是函数式接口
@FunctionalInterface
interface Test5 {
void test5();
default void test() {
}
}
//这个接口中有toString方法,是Object类定义的方法
//实现类在实现接口的时候toString是可以不重写的,因为可以从父接口继承,所以这个方法不是必须实现的
//这个toString方法之外只有一个必须实现的抽象方法,所以是函数式接口
@FunctionalInterface
interface Test6{
void test6();
String toString();
}
}
2. Lambda表达式的基础语法
说了函数式接口,就是因为Lambda只能简化函数式接口的实现。
lambda表达式本质来说就是一个匿名函数,因此在写lambda表达式的时候,不需要关系方法名是什么 实际上,我们在写lambda表达式的时候,也不需要关心返回值类型 我们只需要关注两个部分内容即可: 参数列表和方法体
2.1 基础语法:
(参数) ->{
方法体
};
参数部分 : 方法的参数列表,要求和实现的接口中的方法参数部分一致,包括参数的数量和类型。 方法体部分 : 方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
->: 分隔参数部分和方法体部分。
下面列出各种有参或无参,有返回值或无返回值的Lambda表达式的实现
接口准备:
//无参,没有返回值的函数式接口
@FunctionalInterface
interface NoneReturnNoneParameter {
void test();
}
//一个参数,没有返回值的函数式接口
@FunctionalInterface
interface NoneReturnSingleParameter {
void test(int a);
}
@FunctionalInterface
//多个参数,没有返回值的函数式接口
interface NoneReturnMultipleParameter {
void test(int a,int b);
}
@FunctionalInterface
//无参数,有返回值的函数式接口
interface SingleReturnNoneParameter {
int test();
}
@FunctionalInterface
//有一个参数,有返回值的函数式接口
interface SingleReturnSingleParameter {
int test(int a);
}
@FunctionalInterface
//有多个参数,有返回值的函数式接口
interface SingleReturnMultipleParameter {
int test(int a,int b);
}
Lambda表达式实现:
public static void main(String[] args) {
// 1.实现无参数,无返回值的函数式接口
NoneReturnNoneParameter lambda1 = () -> {
System.out.println("这是一个无参,无返回值的方法");
};
lambda1.test();
// 2.实现一个参数,无返回值的函数式接口
NoneReturnSingleParameter lambda2 = (int a) -> {
System.out.println("这是一个参数,无返回值的方法,参数a:" + a);
};
lambda2.test(10);
// 3,实现多个参数,无返回值的函数式接口
NoneReturnMultipleParameter lambda3 = (int a, int b) -> {
System.out.println("这是多个参数,无返回值的方法,参数 a:" + a + "b =" + b);
};
lambda3.test(100, 200);
// 4,实现无参,有返回值的函数式接口
SingleReturnNoneParameter lambda4 = () -> {
System.out.println("这是无参数,有返回值的方法,返回值是:"+ 10);
return 10;
};
int ret1 = lambda4.test() ;
System.out.println(ret1);
// 5.实现一个参数,有返回值的函数式接口
SingleReturnSingleParameter lambda5 = (int a) -> {
System.out.println("这是一个参数,有返回值的方法,返回值是:" + a);
return a;
};
int ret2 = lambda5.test(100);
System.out.println(ret2);
// 6.实现多个参数,有返回值的函数式接口
SingleReturnMultipleParameter lambda6 = (int a,int b) -> {
System.out.println("这是多个参数,有返回值的方法");
return a + b;
};
int ret3 = lambda6.test(100,200);
System.out.println(ret3);
}
2.2参数部分的精简
参数的类型
由于在接口的方法中,已经定义了每一个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致。因此,此时lambda表达式中的参数的类型可以省略不写。
注意事项:
如果需要省略参教的类型,要保证:要省略,每一个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参教类型没有省略。
// 多个参数、无返回值的方法实现
NoneReturnMultipleParameter lambda8 = (a, b) ->
System.out.println("多个参数、无返回值的方法实现 a = " + a + ", b = " + b);
参数的小括号 如果方法的参数列表中的参数数量 有且只有一个 ,此时,参数列表的小括号是可以省略不写的。 注意事项: 只有当参数的数量是一个的时候,多了、少了都不能省略 省略掉小括号的同时,必须要省略参数的类型。
// 有参、无返回值的方法实现
NoneReturnSingleParameter lambda9 = (a) ->{
System.out.println("一个参数、无返回值的方法实现 参数是 " + a);
};
3. 函数引用
lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。 如果在lambda表达式中需要处理的逻辑比较复杂,一般情况会单独的写一个方法。在lahda表达式中直接引用这个方法即可。
或者,在有些情况下,我们需要在lambda表达式中实现的逻辄,在另外一个地方已经写好了。此时我们就不需要再单独写一遍,只需要直接引用这个已经存在的方法即可。
函数引用:引用一个已经存在的方法,使其替代lambda表达式完成接口的实现。
3.1 静态方法的引用
语法:
类::静态方法
注意事项:
1. 在引用的方法后面,不要添加小括号。
2. 引用的这个方法,参数(数量、类型) 和 返回值,必须要跟接口中定义的一致。
public class Lambda1 {
static interface Calculate {
int calculate(int a, int b);
}
public static void main(String[] args){
// Calculate calculate = (x,y) -> calculate(x,y);
// 简化 --->
Calculate calculate1 = Lambda1::calculate;
System.out.println(calculate1.calculate( 10, 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;
}
3.2 非静态方法的引用
语法:
对象::非静态方法
注意事项:
1.在引用的方法后面,不要添加小括号。
2.引用的这个方法,参数(数量、类型) 和 返回值,必须要跟接口中定义的一致。
public class Syntax2{
public static void main(String[] args){
// 对非静态方法的引用,需要使用对象完成
SyngleReturnMuitpleParamenter lambda = new Calculator()::calculate;
System.out.println(lambda.test(10,30));
}
private static class Calculator{
public int calculate(int a,int b{
return a > b ? a - b : b - a;
}
}
}
3.3 构造方法的引用
使用场景。如果某一个函数式接口中定义的方法,仅仅是为了得到一个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。语法
。类名::new
。注意事项
。可以通过接口中的方法的参数,区分引用不同的构造方法。
package Lamdba_xuexi;
public class Lambda_xue2 {
private interface GetPersonWithNoneParameter{
Person get();
}
private interface GetPersonWithSingleParameter{
Person get(String name);
}
private interface GetPersonWithMutipleParameter{
Person get(String name,int age);
}
public static void main(String[] args) {
//1,使用lambda表达式,实现GetPersonWithNoneParameter接口
GetPersonWithNoneParameter getPerson = Person::new;
//2.使用lambda表达式,实现GetPersonWithSingleParameter接口
GetPersonWithSingleParameter getPerson2 = Person::new;
//3.使用lambda表达式,实现GetPersonWithMutipleParameter接口
GetPersonWithMutipleParameter getPerson3 = Person::new;
Person person = getPerson.get();
getPerson2.get("");
getPerson3.get( "", 1);
GetPerson getPerson4 = Person::new;
Person qwe1 = getPerson4.get("jack",21);
System.out.println(qwe1.toString());
// 以上语句相当于创建了一个类实现了GetPerson1接口并且创建这个类调用方法
//
// GetPersonObject c = new GetPersonObject();
// Person qwe2 = c.get("tom",18);
// System.out.println(qwe2.toString());
}
}
class Person{
int age;
String name;
public Person(String s) {
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Person(String name, int age) {
this.age = age;
this.name = name;
System.out.println("2个参数的构造方法调用了");
}
public Person(){
System.out.println("无参的构造方法调用了");
}
public Person(int age) {
this.age = age;
System.out.println("1个参数的构造方法调用了");
}
}
class GetPersonObject implements GetPerson{
@Override
public Person get(String name, int age) {
return new Person(name,age);
}
}
interface GetPerson{
Person get(String name ,int age);
}
3.3 对象的特殊引用
如果在使用lambda表达式,实现某些接口的时候。lambda表达式中包含了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。其他的参数,可以作为调用方法的参数。此时,可以对这种实现进行简化。
package Lamdba_xuexi;
public class Syntax {
public static void main(String[] args) {
// 如果对于这个方法的实现逻辑,是为了获取到对象的名字
GetField field = person -> person.getName();
// 对于对象方法的特殊引用
GetField field2 = Person::getName;
// 如果对于这个方法的实现逻辑,是为了给对象的某些属性进行赋值
SetField lambda = (person, name) -> person.setName(name);
SetField lambda2 = Person::setName;
// 如果对于这个方法的实现逻辑,正好是参数对象的某一个方法
ShowTest lambda3 = person -> person.show();
ShowTest lambda4 = Person::show;
ShowTest2 lambda5 = (person,a,b) -> person.show(a,b);
ShowTest2 lambda6 = Person::show;
}
interface ShowTest2 {
void test(Person person,int a,int b);
}
interface ShowTest {
void test(Person person);
}
interface SetField {
void set(Person person, String name) ;
}
interface GetField {
String get(Person person) ;
}
}
class Person{
public void show(int a, int b) {
}
public void show() {
}
int age;
String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String s) {
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Person(String name, int age) {
this.age = age;
this.name = name;
System.out.println("2个参数的构造方法调用了");
}
public Person(){
System.out.println("无参的构造方法调用了");
}
public Person(int age) {
this.age = age;
System.out.println("1个参数的构造方法调用了");
}
}
4.Lambda表达式注意细节
当局部变量被一个代码段引用的时候,形成一个闭包。此时的局部变量被默认是final类型的值
package Lamdba_xuexi;
public class LambdaAttention {
static int y = 10;
public static void main(String[] args) {
int x = 10;
LambdaTest lambda = () ->{
System.out.println("x = " + x);
System.out.println("y = " + y);
};
// x = 1; 当局部变量被一个代码段引用的时候,形成一个闭包。此时的局部变量被默认是final类型的值
y = 1; //这里修改静态属性却没有影响
}
}
interface LambdaTest{
void test();
}