Java Lambda 表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法
lambda 表达式的语法格式如下:
(parameters) -> expression 或 (parameters) ->{ statements; }
lambda表达式的重要特征:
- **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
- **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
- **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
- **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
Lambda 表达式实例
Lambda 表达式的简单例子:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
在 Java8Tester.java 文件输入以下代码:
Java8Tester.java 文件
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
使用 Lambda 表达式需要注意以下两点:
- Lambda 表达式主要用来定义行内执行的方法类型接口(例如,一个简单方法接口)。在上面例子中,我们使用各种类型的 Lambda 表达式来定义 MathOperation 接口的方法,然后我们定义了 operation 的执行。
- Lambda 表达式免去了使用匿名方法的麻烦,并且给予 Java 简单但是强大的函数化的编程能力。
变量作用域
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
在 Java8Tester.java 文件输入以下代码:
Java8Tester.java 文件
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]){
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Runoob");
}
interface GreetingService {
void sayMessage(String message);
}
}
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
Hello! Runoob
我们也可以直接在 lambda 表达式中访问外层的局部变量:
Java8Tester.java 文件
public class Java8Tester {
public static void main(String args[]) {
final int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2); // 输出结果为 3
}
public interface Converter<T1, T2> {
void convert(int i);
}
}
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//报错信息:Local variable num defined in an enclosing scope must be final or effectively
final
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
总结:
1.Lambda表达式的使用条件
①必须对应函数式接口(有@functional interface)
②或接口中只有一个抽象方法的接口
2.Lambda表达式的变量要求
①只能引用标记了 final 的外层局部变量,不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
②lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
3.Lambda表达式的存在意义
①函数编程思想:
函数式编程强调程序的执行结果比执行过程更重要。关注于描述问题,而不是怎么实现,隐藏实现细节。
化繁为简。利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算。
4.Lambda表达式的表现形式
1.变量的形式:变量的类型为函数式接口,就么可以复制一个Lambda表达式【不常用】
// 变量的形式
Runnable r = ()->{
System.out.println("任务代码");
};
// 函数式接口类型的变量
Thread t = new Thread(r);
2.参数的形式:方法的形参类型为函数式接口,就可以传入一个Lambda表达式【常用】
// 变量的形式-比较器
Comparator<Integer> comparable = (o1, o2)->{return o2 - o1;};
// 创建集合
ArrayList<Integer> list = new ArrayList<>();
// 存入数据
Collections.addAll(list,11,22,33,44,55);
// 将函数式接口类型 的 形参类型,传给Collections
Collections.sort(list,comparable);
3.返回值的形式:方法的返回值类型为函数式接口,就可以返回一个Lambda表达式【常用】
// 定义一个方法
public static Comparator<Integer> getComparator(){
return (Integer o1,Integer o2)->{return o2-o1;};
}
public static void main (String[] args) {
// 返回值形式
Collections.sort(list,getComparator());
}
利用lambda表达式对自定义对象进行排序
//1.创建三个gf的对象
GirlFriend gf1 = new GirlFriend("xiaoshishi",18,1.67);
GirlFriend gf2 = new GirlFriend("xiaodandan",19,1.72);
GirlFriend gf3 = new GirlFriend("xiaohuihui",19,1.78);
//2.定义数组存储女朋友信息
GirlFriend[] GFarr = {gf1,gf2,gf3};
//3.利用arrays中是sort方法排序 (在这里使用强转会有一定问题:0.1的返回值是0)
//使用匿名内部类的写法:
Arrays.sort(GFarr, new Comparator<GirlFriend>() {
@Override
public int compare(GirlFriend o1, GirlFriend o2) {
//按照年龄大小进行排序(大的在前),若年龄一样,则按照身高排序(高的在前)
return (o1.getAge() == o2.getAge())?(int)(o1.getHeight()-o2.getHeight()):(o2.getAge()-o1.getAge());
}
});
//输出
System.out.println(Arrays.toString(GFarr));
//使用lambda表达式:
//按照年龄大小进行排序(小的在前),若年龄一样,则按照身高排序(矮的在前)
Arrays.sort(GFarr,(o1,o2)->(o1.getAge() == o2.getAge())?(int)(o2.getHeight()-o1.getHeight()):(o1.getAge()-o2.getAge()));
//输出
System.out.println(Arrays.toString(GFarr));
爬楼梯问题
可爱的小明特别喜欢爬楼梯,他有的时候一次爬一个台阶,有的时候一次爬两个台阶
1.如果这个楼梯有20个台阶,小明一共有多少种爬法呢
//本质上是斐波那契数列的变式
System.out.println(getSum1(20));
public static int getSum1(int index){
return (index == 1 ) ? 1:((index == 2)?2:((getSum1(index -1)+getSum1(index -2))));
}
2.如果小明一次一个台阶,有时候两个台阶,有时候三个台阶,小明一共有多少种爬法?
//也是类似斐波那契数列,不过要考虑有多少种情况
System.out.println(getSum2(20));
public static int getSum2(int index){
//如果是三个爬法,则有四种情况:
//如三个台阶时:111 12 21 3
if(index == 1)return 1;
if(index == 2)return 2;
if(index == 3)return 4;
return getSum2(index -1)+getSum2(index -2)+getSum2(index -3);
}
猴子吃桃子
有一堆桃子,猴子第一天吃了其中的一半,并多吃了一个!以后每天猴子都吃当前剩下来的一半,然后
再多吃一个,第10天的时候(还没吃),发现只剩下一个桃子了,请问,最初总共多少个桃子?
System.out.println("初始有" + getSum(10) + "个桃子。");
public static int getSum(int day) {
if (day == 1)
return 1;
else
return (getSum(day - 1) + 1) * 2;
}