lambda 表达式学习笔记 - bmilk
source link: https://www.cnblogs.com/bmilk/p/16818999.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
- 函数式接口
- lambda 表达式简介
- 理解lambda表达式——匿名内部类
- lambda表达式语法
- lambda行为参数化
- lambda表达式的作用域
- this 和 super ,lambda表达式真不是匿名内部类
- 变量作用域
- java8+中的函数式接口
函数式接口
如果一个Java
接口类包含且仅包含一个无默认实现的方法定义,那么他被称为函数式接口。这个方法定义了接口的预期用途。@FunctionalInterface
注解指示一个接口类是一个函数接口,不允许有多个没有默认实现的接口,但不强制要求在接口上添加该注解
示例1:定义一个函数式接口
@FunctionalInterface
public interface MyFunctionalInterface {
// the single abstract method
void function();
}
示例1中有且仅有没有默认实现的方法定义——function()
, 因此它是一个功能接口。
lambda 表达式简介
理解lambda表达式——匿名内部类
lambda
表达式本质上是一个匿名函数,用于实现一个函数接口定义的功能(不能理解的话可以看作是一个匿名对象,后面会专门说区别)。
lambda表达式语法
lambda
表达式的一般语法是:
(Parameters) -> { Body }
Parameters
为函数的入参,放在()
中,与方法定义相同,lambda
表达式的实现放在{}
中
注意
- 参数的类型声明是可选的,编译器可以统一识别参数类型。明确声明参数类型的
lambda
表达式称为显示lambda
表达式,反之称之为隐式lambda
表达式 ()
是可选的,当参数有且仅有一个时,可以省略小括号,否则必须保留{}
是可选的,如果实现的主题只有一条语句,则可以省略,反之必须保留- 返回关键字
retuen
是可选的,在省略{}
的同时,如果表达式有返回值可以省略关键字retuen
// 给定一个值x,返回其的3倍
(int x) ->{return x*3;};
// 省略可选项后定义如下:
x -> x * 3;
lambda行为参数化
可以将lambda
表达式作为参数传递给方,就像传递一个普通的对象一样
示例3:
public class Test {
public static void main(String[] argv) {
doWork((x,y)-> x + y, 4, 2);
}
// (x,y)-> x + y 即为Function 函数接口的实现
private static int doWork(Function function, int x, int y){
return function.cal(x,y);
}
// 定义一个函数接口, 对两个整形进行计算
@FunctionalInterface
interface Function{
int cal(int x, int y);
}
}
lambda表达式的作用域
this 和 super ,lambda表达式真不是匿名内部类
lambda
表达式使用时不会生成新的对象,也就不会产生自己的范围。
如果lambda
表达式中使用关键字this
和super
,他的行为与在执行lambda
表达式的方法中使用this
和super
的行为完全相同。
public class Test {
public static void main(String[] argv) {
Test test = new Test();
test.test();
}
private void test(){
MyFunctionalInterface functionNewClass = new MyFunctionalInterface(){
@Override
public void function(int x) {
System.out.println("匿名内部类this======= "+ this);
}
};
MyFunctionalInterface functionLambda = x -> System.out.println("lambda表达式中执行this ========== " + this);
System.out.println("方法中执行this======= "+ this);
functionNewClass.function(1);
functionLambda.function(1);
}
}
// 执行结果
方法中执行this======= cn.bmilk.Test@4bf558aa
匿名内部类this======= cn.bmilk.Test$1@2d38eb89
lambda表达式中执行this ========== cn.bmilk.Test@4bf558aa
示例3的结果显示,lambda
表达式中调用this
指针和在方法test()
中调用this
指代的是同一个对象cn.bmilk.Test@4bf558aa
, 而使用匿名内部类this
指代的是当前匿名内部类产生的对象cn.bmilk.Test$1@2d38eb89
,所以lambda表达式和匿名内部类是不同的
变量作用域
lambda
表达式可以使用`标记了`final
的外层局部变量,这就是说在lambda
表达式中不能修改外层定义的局部变量(不包含全局变量),否则会编译错误。
示例5:引用外层局部变量
private void test(){
int num = 2;
MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
functionLambda.function(1);
}
示例5:
test*()
中定义了一个变量num
,并且在functionLambda
中进行了引用。前面提到lambda
表达式使用外层局部变量,外层局部变量需要定义为final
,但这里并没又定义为final
,这是因为num
虽然没有被定义为final
,但是在num
被定义之后并没有再次改变,看起来和一个被final
修饰的变量一样,即含有隐性的final
定义。
当尝试再后面修改num
的值,编译器会提示错误信息,如示例6
private void test(){
int num = 2;
num = 3;
// 编译器提示 引用外层局部变量,外层局部变量必须为final或者等同于final
// Variable used in lambda expression should be final or effectively final
MyFunctionalInterface functionLambda = x -> System.out.println(x * num);
functionLambda.function(1);
}
之所以引用外层变量必须是
final
是因为,局部变量被定义再栈帧中,java
访问局部变量的时候实际上是访问他的副本,多个线程对应了多个栈,当lambda
表达式执行线程与定义线程不是同一个情况下,如果变量不被定义为final
,当一个线程改变了变量值,另一个有可能会读取不到最新值导致出错。
java8+中的函数式接口
java8
及更高版本内置了一系列的函数式接口,被放在java.util.function
包下,用于支持j函数式编程。另外在之前的版本中存在的java.lang.Runnable
、java.util.Comparator
也都可以用于函数式编程
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK