1.概述
Lambda表达式是Java8中的新特性,编码时,我们一般尽可能轻量级的将代码封装为数据,传统的解决方案是通过接口和实现类(匿名内部类)实现,这中方式存在语法冗余,this关键字,变量捕捉,数据控制等问题.
例如线程的创建方式:(传统的:)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId());
}
}).start();
使用lambda表达式语法:
new Thread(()->{
System.out.println(Thread.currentThread().getId());
}).start();
二.基础语法
- Lambda表达式在Java语言中引入了一个操作符**“->”**,该操作符被称为Lambda操作符或箭头操作符。它将Lambda分为两个部分:
- 左侧:指定了Lambda表达式需要的所有参数
- 右侧:制定了Lambda体,即Lambda表达式要执行的功能。
组成Lambda表达式的三要素:形式参数,箭头,代码块
Lambda表达式的格式
①、格式:(形式参数) -> {代码块}
②、形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
③、->:由英文中画线和大于符号组成,固定写法。代表指向动作
④、代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
- 语法格式的特征:
①无参,无返回值,Lambda体只需一条语句
public void test01(){
Runnable runnable=()-> System.out.println("Runnable 运行");
runnable.run();//结果:Runnable 运行
}
②Lambda需要一个参数,无返回值
public void test02(){
Consumer<String> consumer=(x)-> System.out.println(x);
consumer.accept("Hello Consumer");//结果:Hello Consumer
}
③Lambda只需要一个参数时,参数的小括号可以省略
public void test02(){
Consumer<String> consumer=x-> System.out.println(x);
consumer.accept("Hello Consumer");//结果:Hello Consumer
}
④Lambda需要两个参数,并且Lambda体中有多条语句。
public void test04(){
Comparator<Integer> com=(x, y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
System.out.println(com.compare(2,4));//结果:-1
}
⑤有两个以上参数,有返回值,若Lambda体中只有一条语句,return和大括号都可以省略不写
public void test05(){
Comparator<Integer> com=(x, y)-> Integer.compare(x,y);
System.out.println(com.compare(4,2));//结果:1
}
⑥Lambda表达式的参数列表的数据类型可以省略不写,因为JVM可以通过上下文推断出数据类型,即“类型推断”
public void test06(){
Comparator<Integer> com=(Integer x, Integer y)-> Integer.compare(x,y);
System.out.println(com.compare(4,2));//结果:1
}
(类型推断:在执行javac编译程序时,JVM根据程序的上下文推断出了参数的类型。Lambda表达式依赖于上下文环境。)
三.函数式接口
1.定义:只包含一个抽象方法的接口,就称为函数式接口。
我们可以通过Lambda表达式来创建该接口的实现对象。
我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以用于检测它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
2.自定义函数式接口
public class FunctionInterface{
//按照函数式接口的定义,自定义一个函数式接口
@FunctionalInterface
public interface MyFunction<T>{
public T getValue(String str);
}
//定义一个方法将函数式接口作为方法参数
public String toLowerString(MyFunction<String> str,String origin){
return str.getValue(origin);
}
//将Lambda表达式实现的接口作为参数传递。
@Test
public void test(){
String value = toLowerString((str)->{
return str.toString();
},"ABC");
System.out.println(value); //ABC
}
}
四.lambda表达式的作用域
Lambda表达式可以看作是匿名内部类实例化的对象,Lambda表达式对变量的访问限制和匿名内部类一样,因此Lambda表达式可以访问局部变量、局部引用,静态变量,实例变量。
1.访问局部变量
public class TestFinalVariable {
interface VarTestInterface{
Integer change(String str);
}
public static void main(String[] args) {
//局部变量不使用final修饰
Integer tempInt = 1;
VarTestInterface var = (str -> Integer.valueOf(str+tempInt));
//再次修改,不符合隐式final定义
tempInt =2;
Integer str =var.change("111") ;
System.out.println(str);
}
}
上面的代码会报错(Error:(12, 60) java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量),即final
特殊情况下,局部变量也可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
例如上面的代码确保Lambda表达式后局部变量后面不做修改,就可以成功啦!
2.访问局部引用,静态变量,实例变量
Lambda表达式不限制访问局部引用变量,静态变量,实例变量。代码测试都可正常执行,代码:
public class LambdaScopeTest {
/**
* 静态变量
*/
private static String staticVar;
/**
* 实例变量
*/
private static String instanceVar;
@FunctionalInterface
interface VarChangeInterface{
Integer change(String str);
}
/**
* 测试引用变量
*/
private void testReferenceVar(){
ArrayList<String> list = new ArrayList<>();
list.add("111");
//访问外部引用局部引用变量
VarChangeInterface varChangeInterface = ((str) -> Integer.valueOf(list.get(0)));
//修改局部引用变量
list.set(0,"222");
Integer str =varChangeInterface.change("");
System.out.println(str);
}
/**
* 测试静态变量
*/
void testStaticVar(){
staticVar="222";
VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+staticVar));
staticVar="333";
Integer str =varChangeInterface.change("111") ;
System.out.println(str);
}
/**
* 测试实例变量
*/
void testInstanceVar(){
instanceVar="222";
VarChangeInterface varChangeInterface = (str -> Integer.valueOf(str+instanceVar));
instanceVar="333";
Integer str =varChangeInterface.change("111") ;
System.out.println(str);
}
public static void main(String[] args) {
new LambdaScopeTest().testReferenceVar();
new LambdaScopeTest().testStaticVar();
new LambdaScopeTest().testInstanceVar();
}
}