lambda表达式是java8新增的一个语法元素,也是java8最重要新特性,增加lambda新特性是java语言持续保持活力和创新性的必然要求,因为lambda表达式是近几年计算机语言设计的重点关注领域,包括随spark而火起来的scala语言,也就是以对lambda表达式支持而备受欢迎。可以说lambda表达式是继java5提供泛型编程后,又一次重塑和飞跃。java实现的lambda表达式与scala略有不同,本文只介绍java8中lamda表达式的应用。
记住重要的一点:我们知道一个接口必须有一个子类来实现其所有方法才能使用。lambda表式的作用就是当把lambda表达式(即匿名函数)赋值给一个函数式接口的引用时,自动创建了该接口的匿名的实现类。看完下面的例子就明白了
java8中的lambda表达式和函数式接口密切相关。下面分别介绍下lambda表达式自身的定义和函数式接口的定义,然后再结合具体例子就不难理解lambda表达式和应用了。
**lambda表达式:**lambda表达式本质上就是个匿名方法,但此方法不能单独执行,而要赋给一个函数式接口。lambda表达式的语法很简单,也比较单一,看下面的例子
//无参数形式,其实就是一个函数,返回值就是100
() -> 100
//带参数参数形式(和普通函数一个可以有多个参数,参数类型可以不用指定,java会根据函数式接口的抽像方法自己推断出来)
(int n) -> n%10 //可以这样简写(n) -> n%2
//带多个参数
(n,m,l) -> n+m+l //也可以这样简写(int n,int m,int l) -> n+m+l
//lambda块
(n) -> {
int sum = 0;
for(int i=0;ireturn sum;
};
函数式接口:只包括一个抽像方法的接口就是函数式接口,我们知道在java8之前接口所有的方法都是抽像方法,但java8提供了默认实现的方式,说的简单点函数式接口可以有多个方法,但只能有一个方法没有方法体。看下面的例子就很容易明白了
//java自带的run接口就是函数式接口,此处@FunctionalInterface注解的用处是帮助开发工具进行语法检查,指明此接口是一个函
//数式接口,如果接口的定义不符合函数式接口的定义就会提示错误
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
//此接口有三个方法,但只有其中一个是抽像方法,所以是函数式接口
@FunctionalInterface
public interface TestInterface {
// 抽象方法
public void sub();
// java.lang.Object中的方法不是抽象方法
public boolean equals(Object var1);
// default不是抽象方法
public default void defaultMethod(){
}
// static不是抽象方法
public static void staticMethod(){
}
}
理解了函数式接口和lambda各自的定义与使用后,下面来看几个具体的例子就明白如何具体使用了
例1:无参形式
//定义函数式接口
@FunctionalInterface
public interface LambdaInterface {
double get();
}
public class LambdaDemo {
public static void main(String[] args) {
/**
* 我们知道一个接口必须有一个子类来实现其所有方法才能使用
* lambda表式的作用就是当把() -> 12.34;赋值给一个函数式接口的引用时ref,
* 自动创建了该接口LambdaInterface的匿名的实现类,lambda表达式就是该接口抽像方法的实现
*/
LambdaInterface ref = () -> 12.34;
System.out.println(ref.get());
ref = () -> Math.sqrt(204);
System.out.println(ref.get());
}
}
例2:带参数形式
@FunctionalInterface
public interface LambdaInterface {
double getSum(double a,double b);
}
public class LambdaDemo {
public static void main(String[] args) {
LambdaInterface ref = (a,b) -> a+b;//也可以这样写(double a,double b) -> a+b
System.out.println(ref.getSum(1.2,3.4));
}
}
例3:lambda表达式块,在实际工程中,很少有一个表达式就可以解决问题的,lambda表达式块,其实和普通函数体的写法是一样的
@FunctionalInterface
public interface LambdaInterface{
/**
* 求从1到n-1的和
* @param n
* @return
*/
int getSum(int n);
}
public class LambdaDemo {
public static void main(String[] args) {
/**
* lambda块必须有return语句
*/
LambdaInterface ref = (n) -> {
int sum = 0;
for(int i=0;ireturn sum;
};
System.out.println(ref.getSum(100));
}
}
例4:lambda表达式泛型编程,lambda表达式本身不支持泛型,但可以由函数式接口来实现类型参数泛化
@FunctionalInterface
public interface LambdaInterface{
/**
* 本例展示lambda对泛型编程的支持
* @param para
* @return
*/
T getResult(T para);
public default void defaultMethod(){
return ;
}
}
public class LambdaDemo {
public static void main(String[] args) {
/**
* 求从1到n-1的和
*/
LambdaInterface ref_int = (n) -> {
int sum = 0;
for(int i=0;ireturn sum;
};
System.out.println(ref_int.getResult(100));
/**
* 把传入的字符串颠倒lambda => adbmal
*/
LambdaInterface ref_str = (str) ->{
String result = "";
int i;
for(i = str.length()-1; i >= 0; i--)
result += str.charAt(i);
return result;
};
System.out.println(ref_str.getResult("lambda"));
}
}
例5:把lambda表达式传当作参数传递给函数,关键的一点就是把函数的一个参数定义为函数式接口类型,一般是函数的第一个形参但不是强制性的,这里还是用上面的例子,不必是泛型接口
@FunctionalInterface
public interface LambdaInterface{
/**
* 当函数式接口在下面的程序中定义为getSum和getInvertedStr的参数类型
* @param para
* @return
*/
T getResult(T para);
public default void defaultMethod(){
return ;
}
}
public class LambdaDemo {
/**ref为函数式接口引用,一般放在第一个参数,但不是强制性的
* 求从1到n-1的和
*/
public static int getSum(LambdaInterface ref,int n){
return ref.getResult(n);
}
public static String getInvertedStr(LambdaInterface ref,String str){
return ref.getResult(str);
}
public static void main(String[] args) {
/**
* 求从1到n-1的和
* lambda表达式直接传递给getSum的第一个参数
*/
int result = getSum((n) ->{
int sum = 0;
for(int i=0;ireturn sum;
},100);
System.out.println(result);
/**
* 把传入的字符串颠倒lambda => adbmal
*/
String result1 = getInvertedStr((str) ->{
String result0 = "";
int i;
for(i = str.length()-1; i >= 0; i--)
result0 += str.charAt(i);
return result0;
},"lambda");
System.out.println(result1);
}
}
注意:lambda表达式的参数类型和返回类型必须与函数式接口定义的一致或者兼容