在Java 8中,为了能够将行为参数化而引入了Lambda表达式。
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
Lambda表达式在Java语言中引入了->
操作符,->
操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:
左侧部分指定了Lambda表达式需要的所有参数,Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。
右侧部分指定了Lambda体,即Lambda表达式要执行的功能,Lambda体本质上就是接口方法具体实现的功能。
Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是类型推断
。
public class LambdaTest { public void completedLeft() { // 类型String可以省略,由上下文进行类型推断;因为只有一个参数,小括号可以省略 Consumer<String> consumer = (String e) -> println(e.substring(3, 6)); } public void singleLeft() { Consumer<String> consumer = e -> println(e.substring(3, 6)); } }
return
关键字可以省略public class LambdaTest { public String completeRight() { Supplier<String> supplier = () -> { String result = new Random().nextInt(100) + ""; return result.substring(1); }; return supplier.get(); } public String simpleRight() { Supplier<String> supplier = () -> new Random().nextInt(100) + ""; return supplier.get(); } }
public interface INoArgsAndNoReturn { void execute(); }
public class LambdaTest { public void noArgsAndNoReturn() { INoArgsAndNoReturn lambda = () -> println("execute lambda"); execute(lambda); } private void execute(INoArgsAndNoReturn lambda) { lambda.execute(); } }
public interface INoArgsAndHasReturn { String execute(); }
public class LambdaTest { public void noArgsAndHasReturn() { INoArgsAndHasReturn lambda = () -> "return lambda result"; execute(lambda); } private void execute(INoArgsAndHasReturn lambda) { println(lambda.execute()); } }
public interface IHasArgsAndNoReturn { void execute(Integer one, Integer two); }
public class LambdaTest { public void hasArgsAndNoReturn() { IHasArgsAndNoReturn lambda = (a, b) -> println(a + b); execute(lambda); } private void execute(IHasArgsAndNoReturn lambda) { lambda.execute(1, 2); } }
public interface IHasArgsAndHasReturn { String execute(Integer one, Integer two); }
public class LambdaTest { public void hasArgsAndHasReturn() { execute((x, y) -> x + " " + y); } private void execute(IHasArgsAndHasReturn argsReturn) { println(argsReturn.execute(1, 2)); } }
Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为
final,或事实上是 final 。
第一,实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。
如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分
配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它
的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式,这种模式会阻碍很容易做到的并行处理。