Lambda表达式在菜鸟教程中被这样描述:
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
Lambda表达式的写法为:
( 参数列表... ) -> { 方法体 };
中间的 “ ->” 可以被读作“ goes to ”
比如要传入参数a和b,返回a与5的和,可以写成:
(a) ->{ return a + 5 ;};
可读作 a gose to a + 5,
我认为Lambda表达式看起来更像一个匿名函数。
Lambda表达式被用来更简洁的实现接口,我们知道实现接口有三种基本方式:
假如现有一个USB接口
public interface USB {
public void connect();
}
有键盘,硬盘,手机三个类对USB接口进行不同实现
1)用实现类实现接口
class Keyboard implements USB{
@Override
public void connect() {
System.out.println("连接键盘,进行输入");
}
}
public class Test{
public static void main(String[] args) {
Keyboard keyboard = new Keyboard();
keyboard.connect();
}
}
2)匿名内部类实现接口
public class Test{
public static void main(String[] args) {
//匿名内部类实现接口
USB hardDisk = new USB() {
@Override
public void connect() {
System.out.println("连接硬盘,传输数据");
}
};
hardDisk.connect();
}
}
3)Lambda表达式实现接口
public class Test{
public static void main(String[] args) {
//Lambda表达式实现接口
USB phone = () -> {
System.out.println("连接手机,给手机充电");
};
phone.connect();
}
}
我们可以看出Lambda表达式实现接口的方式简洁明了,吊打前两种实现方式,但是Lambda只能实现有且仅有一个抽象方法的接口,这样的接口也叫做函数式接口,函数式接口可以被隐式转换为 Lambda 表达式,而且Lambda表达式中没有体现接口中抽象方法的名字,所以我认为Lambda表达式更像一个匿名函数。定义函数时接口时可以在接口前加上@FunctionalInterface注解。
Lambda表达式的语法为
接口 引用名 = (参数列表...) -> { 方法体;};
假设现在我们有4个函数式接口,分别包含无参数无返回值方法,无参数有返回值方法,有参数无返回值方法,有参数有返回值方法
1)无参数无返回值方法
@FunctionalInterface
public interface NoParameterNoReturnedValue {
public abstract void method();
}
2)无参数有返回值方法
@FunctionalInterface
public interface NoParameterOneReturnedValue {
public abstract int method();
}
3)有参数无返回值方法
@FunctionalInterface
public interface OneParameterNoReturnedValue {
public abstract void method(String str);
}
4)有参数有返回值方法
@FunctionalInterface
public interface OneParameterOneReturnedValue {
public abstract int method(int num);
}
用Lambda表达式实现接口
public class LambdaTest {
public static void main(String[] args) {
//无参无返回
NoParameterNoReturnedValue nn = () -> {
System.out.println("hello world!");
};
//无参返回int 1-10 随机数
NoParameterOneReturnedValue no = () -> {
return (int) (Math.random()*10);
};
//有String类参数无返回值 拼接字符串在控制台输出
OneParameterNoReturnedValue on = (String name) -> {
System.out.println("Hi " + name + "!");
};
//有int参数,返回int值加10
OneParameterOneReturnedValue oo = (int num) -> {
return num + 10;
};
nn.method();
System.out.println( no.method() );
on.method("Jack");
System.out.println( oo.method(5) );
}
}
//output:
// hello world!
// 3
// Hi Jack!
// 15
而实际上,我们可以对Lambda表达式进行更简略地描写:
1)如果方法体里只有一个语句,我们可以省略包围方法体的花括号,如果这一个语句恰好是返回一个表达式的return语句,我们甚至可以省略return关键字;
2)在参数列表中,我们可以不显式地声明参数类型,编译器可以统一识别参数值。假如接口中的抽象方法只有一个参数,那么我们也可以省略包围参数列表的圆括号;
对于上面的有参有返回值接口的实现,我们可以这样写:
OneParameterOneReturnedValue oo = num -> num + 10;
Lambda表达式似乎可以引用外围的变量
public class LambdaTest {
public static void main(String[] args) {
int num = 10 ;
OneParameterOneReturnedValue oo = myNum -> num + myNum;
System.out.println(oo.method(5));
}
}//output:
//15
在这里,在Lambda表达式中可以引用外围的变量num,并返回了num值与我输入的myNum值的和,但是可能经过Lambda表达式引用过的变量就被修饰为final变量了,当我无论在Lambda表达式中修改引用的num值或者在Lambda表达式之后修改num的值,编译器都会报这样的错误:
int num = 10 ;
OneParameterOneReturnedValue oo = myNum -> {
num ++;//报错
return num + myNum;
};
System.out.println(oo.method(5));
int num = 10 ;
OneParameterOneReturnedValue oo = myNum -> num + myNum;
System.out.println(oo.method(5));
num += 10 ;//报错
报错:
Variable used in lambda expression should be final or effectively final
我也不是很明白其中的道理
对于使用Runnable接口开辟一个新线程的时候,我们可以使用Lambda表达式实现Runnable接口:
Runnable runnable = () -> {
int countdown = 10;
while ( countdown-- >0)
System.out.println(countdown>0 ? "倒计时"+ countdown +"秒" : "发射!");
};
new Thread(runnable).start();
或者直接:
new Thread(() -> {
int countdown = 10;
while ( countdown-- >0)
System.out.println(
countdown>0 ? "倒计时"+ countdown +"秒" : "发射!"
);
}).start();
这大概就是在开头所提到的
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
这样就相当于直接把Runnable的具体实现传入Thread的构造器中了。