前言
逆水行舟,不进则退!!!
目录
函数式接口与lambda表达式
变量捕获
补充
函数式接口:一个接口有且只有一个抽象方法。 注意: 1,如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口 2,@FunctionalInterface 注解 加在接口前,那么编译器就会按照函数式接口的定义来要求该接口,如果有两个及以上的抽象方法,编译程序就会报错。这个注解可以不加,加上是会自动进行检测 |
形如:
@FunctionalInterface
interface NoParameterNoReturn {
//只有这一个抽象方法
void test();
}
注意,函数式接口限制的是:有且只能有一个抽象方法,也就意味着函数式接口也可以有静态方法,普通方法 。如下:
@FunctionalInterface
interface NoParameterNoReturn {
//只有这一个抽象方法
void test();
//静态方法
static void test1() {
}
// 普通方法
default void test2() {
}
}
在不使用lambda表达式时,我们实现一个接口是通过匿名内部类的方式来完成。如下:
//抽象方法无返回值无参数 的函数式接口
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("hello");
}
};
noParameterNoReturn.test();
}
解释代码:我们创建了一个匿名子类 new NoParameterNoReturn() { }, 它继承自NoParameterNoReturn 这个接口,重写了其中的 test方法,然后我们将这个匿名子类的实例赋值给了变量 noParameterNoReturn。通过这个变量,可以调用重写的方法。
接下来我们使用lambda表达式来实现几个接口:
1,无返回值无参数 |
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
//无返回值无参数
void test();
}
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = () -> System.out.println("hello");
noParameterNoReturn.test();
}
2,无返回值一个参数 |
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
//无返回值一个参数
void test(int a);
}
public static void main(String[] args) {
OneParameterNoReturn oneParameterNoReturn = (int a) -> System.out.println(a);
oneParameterNoReturn.test(14);
}
3,无返回值多个参数 |
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
//无返回值多个参数
void test(int a,int b);
}
public static void main(String[] args) {
MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> System.out.println(a+b);
moreParameterNoReturn.test(3,3);
}
4,有返回无参数 |
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
//有返回值无参数
int test();
}
public static void main(String[] args) {
NoParameterReturn noParameterReturn = () -> {return 10;};
int ret = noParameterReturn.test();
System.out.println(ret);
}
5,有返回值一个参数 |
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
//有返回值一个参数
int test(int a);
}
public static void main(String[] args) {
OneParameterReturn oneParameterReturn = (a) -> {return a+10;};
int ret = oneParameterReturn.test(4);
System.out.println(ret);
}
6,有返回值多个参数 |
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
// 有返回值多个参数
int test(int a,int b);
}
public static void main(String[] args) {
MoreParameterReturn moreParameterReturn = (a,b) -> {return a+b;};
int ret = moreParameterReturn.test(4,5);
System.out.println(ret);
}
总结:
|
我的理解:
() -> {
};
圆括号中是所需要的参数, “->” 表示的是 这里是一个 lambda表达式, 花括号 中是函数体
1,lambda表达式可以捕获外部变量
我的理解: lambda可以访问到lambda表达式所在的上下文环境中的变量,当Lambda表达式捕获外部变量时,它会在创建的时候将指定的变量复制一份,并在函数体中使用这份副本。尽管lambda表达式可以捕获外部变量,但在lambda表达式内部对这些捕获的变量进行的修改不会影响到原变量的值。
在Java中,lambda表达式的捕获方式 主要分为 值捕获 和 引用捕获。 值捕获主要是针对 外部的局部变量。引用捕获 只要针对的是外部的 实例变量 和 静态变量。
在lambda表达式中,值捕获和引用捕获是两种不同的变量捕获方式。值捕获是将变量的值拷贝到lambda表达式中,这意味着在lambda表达式中使用的是原始变量的副本,对副本的修改不会影响到原始变量。而引用捕获则是将变量的引用拷贝到lambda表达式中,这意味着在lambda表达式中使用的是原始变量的引用,对引用所指向的对象做的修改将影响到原始变量。
需要注意的是,在使用引用捕获时,要确保被引用的对象在 lambda 表达式执行时仍然存在。因为引用捕获传递的是引用,如果后面该对象被销毁了,再通过这个引用去访问就会产生未定义的行为。而值捕获传递的是值的副本,所以在之后对该变量做的修改不会影响到当前已经传入的值。
同时,关于lambda表达式中的参数和被捕获的变量,需要特别理解的是:lambda表达式的“参数”是在被调用时拷贝,而“被捕获的变量的值”是在lambda表达式创建时拷贝。
对于局部变量,Java要求必须是final或者是事实上的final(即在初始化后不会再发生变化)。如果局部变量符合这些条件,那么它们就可以被Lambda表达式捕获并在其中使用。捕获的局部变量在lambda表达式中使用时,不能去修改。
另外,需要注意的是,虽然Lambda表达式可以捕获实例变量,但这并不是通过值传递的方式实现的。实际上,Lambda表达式内部只能访问到实例变量的getter方法,而无法直接访问到实例变量的值。这是为了防止潜在的并发问题和数据不一致情况。
实例变量也被称为对象变量,它是定义在类中,但在方法体(包括构造方法)之外的变量。当基于某个类创建对象时,每个对象都有自己独立的实例变量副本,互不干扰,可以通过对象的引用来访问这些实例变量。
类成员变量是属于类的,所有对象共享同一个类成员变量,在使用时可以通过类名来访问。被static修饰
静态变量是属于类的,所有对象共享同一个静态变量,在使用时可以直接通过类名来访问。需要注意的是,静态变量只能被赋值一次,并且它的值在整个程序运行期间都是不变的。被static 和 final 修饰
以上呢,都是我通过查找资料找到的,但是呢,当我问在IT行业工作多年的大佬的时候,对方是这样说的:
“ lambda表达式捕获变量,Java要求必须是final或者是事实上的final(即在初始化后不会再发生变化)。如果变量符合这些条件,那么它们就可以被Lambda表达式捕获并在其中使用。捕获的局部变量在lambda表达式中使用时,不能去修改。”
好像并没有强调值捕获引用捕获之类的,只说了只一点,那我也没办法,不好取舍,只好将这些东西都写在这,或许会有用。
如何确定lambda表达式中获取到的this是指向谁?
答:在Java中,Lambda表达式中的this指向的是创建该Lambda表达式的方法中的this。具体来说,当一个对象调用一个方法,该方法返回一个Lambda表达式时,这个Lambda表达式会捕获这个方法的this引用。因此,如果在Lambda表达式中使用了this关键字,那么它指向的就是这个方法的this引用。
此外,需要注意的是,虽然Lambda表达式看起来和一个内部匿名类很相似,但二者有一个主要的区别:在内部匿名类中,this关键字指向的是匿名类本身,而在Lambda表达式中,this关键字指向的是当前类。
什么是捕获列表?
答:捕获列表是C++中lambda表达式的一个特性,它用于定义一个lambda所在函数中局部变量的列表。在默认情况下,lambda表达式内部是不能直接使用外部的变量的,此时,捕获列表就可以起到传递外部数据的作用。捕获列表通常为空,表示lambda不使用它所在函数中的任何局部变量。此外,根据传递的行为,捕获列表也可以分为值捕获和引用捕获两种类型。值捕获 和 引用捕获 不能出现在同一个 捕获列表。总的来说,捕获列表是lambda表达式的一个重要组成部分,它使得lambda能够访问并使用其所在函数的局部变量。
1,注意区分 lambda表达式 和 匿名内部类:
Lambda表达式和匿名内部类在Java中都用于简化代码书写,但二者具有显著的不同。匿名内部类是一个继承自具体类或抽象类的子类,它可以用来创建任何类型的接口的实例,而Lambda表达式则是用来创建函数式接口的实例。
它们的主要区别还体现在以下几点:首先,所需的类型不同。匿名内部类可以是接口、抽象类或具体类的子类,而Lambda表达式只是针对函数式接口。其次,使用限制也存在差异。如果接口中仅有一个抽象方法,既可以使用Lambda表达式,也可以使用匿名内部类。但是,如果接口中有多个抽象方法,则只能使用匿名内部类。
此外,Java Lambda表达式的一个重要用途是简化某些匿名内部类的写法。例如,我们可以通过下列两种方式来创建并启动一个新线程:
// 使用匿名内部类的方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread using anonymous inner class");
}
}).start();
// 使用Lambda表达式的方式
new Thread(() -> System.out.println("Thread using lambda expression")).start();
对比上述两种方法,可以发现Lambda表达式的写法更加简洁。
2,lambda表达式的优缺点:
优点:
1,lambda表达式可以简化代码量,代码更精简,减少了冗余;
2,减少了匿名内部类的创建,每个匿名内部类都会生成一个 .class 文件,而lambda表达式则不会。所以lambda表达式也是节省了资源。
缺点:
1,不利于后期维护。lambda表达式是匿名的,可读性差,对与维护工作来说,增加了不少难度。
2,学习成本更高,lambda表达式不容易理解,相比匿名内部类来说,需要更多的时间去学习。
我是专注学习的章鱼哥~