官方说明
在学习一个知识点的时候,首先需要知道的是学习该知识点有什么用,能帮我们做些什么?同样地,学习Lambda表达式就要知道Lambda表达式可以帮助我们做什么,也就是为什么要使用lambda表达式?我觉得官方对Lambda表达式的部分重要的描述如下:
- Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.
- Lambda expressions let you express instances of single-method classes more compactly.
在描述lambda表达式的时候,官方首先就是利用了匿名类作为切入点,拉开了Lambda表达式在Java8中的面纱。Lambda表达式的功能之一就是可以简化大部分的匿名内部类的,它具备代替大部分的匿名内部类的能力,函数式编程让我们的Java代码变得更加优美。不信?往下看!
简单案例1:启动线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类启动线程");
}
}).start();
学习过Java 的同学一眼都能知道上面是使用匿名内部类的方式来创建一个线程,然而用Lambda表达式将上面的代码进行改写就变成了下面的形式。
new Thread(() -> {
System.out.println("使用Lambda表达式启动线程");
}).start();
简单案例2:遍历List数据
首先定义一个List:
List<String> demoList = newArrayList<String>(Arrays.asList("str1", "str2"));
传统遍历打印List的方式有三种
使用for语句
for(int index=0;index<demoList.size();index++){
System.out.println(demoList[index]);
}
使用增强for
for(String strObj:demoList){
System.out.println(strObj);
}
使用Iterator迭代器
Iterator<String> iterator = demoList.iterator();
while(iterator.hasNext()){
String obj = iterator.next();
System.out.println(obj);
}
使用Lambda表达式改写之后:
demoList.forEach(System.out::println);
如果觉得案例不够有代表性的话,可以到去看看官网给出的例子,同样的功能,使用了多种的实现方式(包括普通变量参数、类参数、匿名内部类,最后是Lambda表达式等多种写法),保证你看得清晰( 不想看英文的,可以只看代码 )!(官方例子)。
是不是觉得使用Lambda表达式会匿名内部类更加优美呢?另外还能在不会Lambda表达式的人面前装13呢!(但也不是说匿名内部类没有任何优点,要不然就能替换全部的匿名内部类了嘛)
如何觉得对Lambda表达式有点兴趣的话,别急这才刚刚开始呢。
一、Lambda语法
Lambda表达式主要有以下三部分组成:
- A comma-separated list of formal parameters enclosed in parentheses.
- you can only use lambda expressions in situations in which the Java compiler can determine a target type
- The arrow token, ->
- A body, which consists of a single expression or a statement block.
- you can only use lambda expressions in situations in which the Java compiler can determine a target type
- A return statement is not an expression; in a lambda expression, you must enclose statements in braces ({}). However, you do not have to enclose a void method invocation in braces.
- A lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name.
简单总结:( 可以不看上面的英文描述,直接看中文总结 ):
1、没有参数的时候必须要用“()”() -> { 语句 }
多个参数用“()”包裹,同时用“,”进行分割 (数据类型 参数1,数据类型 参数2,...) ->{ 语句 }
2、在Lambda表达式中,可以忽略参数的数据类型,(参数1...) ->{ 语句 }
另外,当只有一个参数的时候还可以忽略掉“()”参数1 ->{ 语句 }
3、需要返回值的,可以使用return语句
s -> {
return s.getGender() == "女" && s.getAge() >= 18 && s.getAge() <= 25;
}
4、在使用Lambda表达式的时候,如果返回值不是一个表达式,那必须要使用“{}”将代码包裹(参数1,参数2,...) ->{ 语句 }
但是Void方法则不需要:参数1 -> System.out.println(参数1)
二、变量作用域
Like local and anonymous classes, lambda expressions can capture variables; they have the same access to local variables of the enclosing scope. However, unlike local and anonymous classes, lambda expressions do not have any shadowing issues (see Shadowing for more information). Lambda expressions are lexically scoped. This means that they do not inherit any names from a supertype or introduce a new level of scoping. Declarations in a lambda expression are interpreted just as they are in the enclosing environment.
像局部类和匿名类一样,lambda表达式可以捕获变量;它们对外围作用域的局部变量具有相同的访问权限。然而,与局部和匿名类不同,lambda表达式没有任何隐藏问题。Lambda表达式在词法上有作用域。这意味着它们不会从超类型继承任何名称,也不会引入新的作用域级别。lambda表达式中的声明被解释为与它们在封闭环境中的一样。
总结:
- 可以直接在 lambda 表达式中访问外层的局部变量。
- lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改。
- 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
三、目标类型
you can only use lambda expressions in situations in which the Java compiler can determine a target type.
- Variable declarations
- Assignments
- Return statements
- Array initializers
- Method or constructor arguments
- Lambda expression bodies
- Conditional expressions, ?:
- Cast expressions
案例:
首先定义两个只有一个方法的接口。
public interface Runnable {
void run();
}
public interface Callable<V> {
V call();
}
然后定义重载方法
void invoke(Runnable r) {
r.run();
}
<T> T invoke(Callable<T> c) {
return c.call();
}
最后调用
String s = invoke(() -> "done");
问题来了,最后调用的是哪一个方法呢?答案就是Callable
四、序列化
You can serialize a lambda expression if its target type and its captured arguments are serializable. However, like inner classes, the serialization of lambda expressions is strongly discouraged.
如果lambda表达式的目标类型及其捕获的参数可序列化,则可以序列化该表达式。但是,与内部类一样,强烈反对lambda表达式的序列化。
拓展 :在对Java集合操作的时候使用Stream和Lambda表达式相结合使用。
案例1:查询所有学生列表,然后按照性别进行分组。代码如下:
List<Student> demoList = studentMapper.getList();
Map<String, List<Student>> collectMap = demoList.stream().collect(
Collectors.groupingBy(Student::getGender));
案例2:查询所有学生列表,然后筛选出年龄等于18并且性别为女的所有学生。代码如下:
List<Student> demoList = studentMapper.getList();
List<Student> collectList = demoList.stream().filter(
o->o.getAge()==18 && Objects.equals(o.getGender(),"女")
).collect(Collectors.toList());
案例3:按照年纪进行排序
List<Student> demoList = studentMapper.getList();
List<Student> collectList = demoList.stream().sorted(
Comparator.comparing(Student::getAge)).collect(Collectors.toList());
案例4:求所有的学生的年龄和、最大年龄、最小年龄、平均年龄。(int->mapToInt 、double -> mapToDouble…)
定义数据: List<Student> demoList = studentMapper.getList();
查询总和: double** **sum = demoList.stream().mapToDouble(Student::getOweTuition).sum();
查询最大年龄: demoList.stream().mapToInt(Student::getAge).max().getAsInt();
查询最小年龄: demoList.stream().mapToInt(Student::getAge).min().getAsInt();
查询平均年龄: demoList.stream().mapToInt(Student::getAge).average().getAsInt();
案例5:遍历Map集合然后进行操作
demoMap.forEach( (k,v)->{ 代码块 }
其余的案例以后更新,自己去看JDK文档也可以,也就是各种方法的调用而已!