简介
- 什么是Lambda表达式?
- Lambda是java8引入的新特性,是Java函数式编程的基础,减少了代码长度和代码复杂度。
- 理解: 他就是一个没有名字的函数,那这不就是匿名函数吗?但是写Lambda表达式可比写匿名函数方便的多。
- 特点: 允许将函数当作参数传递,叫做行为参数化,这里的传递的参数也是Function函数式接口,与函数式接口结合使用,效果更佳。
匿名内部类
在了解lambda之前,先来看看不使用Lambda表达式时,如何使用匿名内部类简化代码。关于匿名内部类的详解,参考文章, 想了解为什么要使用内部类可以看这篇:为什么要使用内部类。
- 为什么要使用匿名内部类?
- 最基础的方法调用是:新建一个中间类去继承接口/抽象类/普通类,实现或者重写接口,然后在实例化这个中间类,去调用这个类中实现或者重写方法,但其实这个中间类是没必要创建的。
- 匿名内部类就是没有名字的内部类,但是一个类应该具有的成员,它都是具备的。虽然也叫类,不过编写代码时,是没有显示创建类的。
- 没有名字那么如何去new对象出来呢?
- 匿名调用需要继承父类或者实现接口中的方法来达到这一目的。
- 无需重新去新建一个类实现或者继承接口,再去实例化这个类进行调用,省去了重新创建类的步骤。
public class LamdbaTest {
public static void main(String[] args) {
System.out.println("================调用内部类时,传递的参数可以是 接口 / 抽象类 / 普通类================");
// 注:这里不是实例化接口,而是创建一个接口的实现类。
new Inter() {
@Override
public void show() {
System.out.println("接口Inter");
}
}.show();
// 抽象类
new People() {
@Override
void method() {
System.out.println("抽象类People");
}
}.method();
// 普通类
new Employee() {
@Override
void work() {
System.out.println("具体类Employee");
}
}.work();
}
}
// 接口
interface Inter {
void show();
}
// 抽象类
abstract class People {
abstract void method();
}
// 普通类
class Employee {
void work(){
System.out.println("雇员要工作");
}
}
- 当内部类中有多个方法时,该如何调用?
这里就要用到多态了,将这个内部类实例化出来,然后在进行调用。
public class LamdbaTest {
public static void main(String[] args) {
// 注意: 这里不是实例化接口,而是创建一个接口的实现类。
Inter inter = new Inter() {
@Override
public void show() {
System.out.println("接口Inter");
}
@Override
public void display() {
System.out.println("来吧,展示!");
}
};
inter.display();
inter.show();
}
}
// 接口
interface Inter {
void show();
void display();
}
Lambda表达式
表达式写法
如下图所示:一个lambda分为三部分,即参数列表 + 操作符 + lambda体。
以下是lambda表达式的重要特征:
- 可选类型声明: 不需要声明参数类型,编译器会自动识别参数值,进行类型推断。例如:
s -> System.out.println(s)(String s) -> System.out.println(s)
- 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。例如:
s -> System.out.println(s)一个参数不需要添加圆括号。(x, y) -> Integer.compare(y, x)两个参数添加了圆括号,否则编译器报错。
- 可选的大括号: 如果主体包含了一个语句,则不需要使用大括号。例如:
s -> System.out.println(s)不需要大括号(s) -> { if (s.equals("s")){ System.out.println(s); } };需要大括号
- 可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要加return语句,指定明表达式返回了一个数值。
方法引用
方法引用是用来直接访问类或者实例已经存在的方法或构造方法,提供了一种引用而不执行方法的方式。
- 当Lambda表达式中只是执行一个方法调用时,直接使用方法引用的形式可读性更高一些。
- 方法引用使用
::操作符来表示,左边是类名或实例名,右边是方法名,方法名无需加()。 - 方法引用有以下三种形式:
- 类 :: 静态方法
- 类 :: 实例方法
- 对象 :: 实例方法
public class TestLambda {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(Student.builder().name("daiaoqi").age(23).sex("man").grade(80).build());
students.add(Student.builder().name("wutong").age(25).sex("woman").grade(78).build());
students.add(Student.builder().name("kanghaike").age(19).sex("woman").grade(98).build());
students.add(Student.builder().name("jieweicheng").age(23).sex("man").grade(89).build());
students.add(Student.builder().name("jieweicheng").age(23).sex("man").grade(89).build());
TestLambda testLambda = new TestLambda();
// 测试类的实例方法引用
testLambda.testConsumer(students);
// 类的静态方法引用
students.stream().forEach(TestLambda::testStaticMethod);
// 对象的实例方法引用
students.stream().forEach(testLambda::testObjectRef);
}
/**
* @Description 类的实例方法引用
* @param students
* @Return
*/
public void testConsumer(List<Student> students){
Consumer<Student> consumer = new Consumer<Student>() {
@Override
public void accept(Student student) {
System.out.println(student.getAge());
}
};
students.stream()
.peek(consumer)
.collect(Collectors.toList());
}
/**
* @Description 对象的实例方法引用
* @param student
* @Return
*/
public void testObjectRef(Student student) {
System.out.println(student.getGrade());
}
/**
* @Description 类的静态方法应用
* @param student
* @Return
*/
public static void testStaticMethod(Student student) {
System.out.println(student.getGrade());
}
}
注意: Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型需要保存一致
二者区别
看了上述lambda的写法之后,在结合第二章节中匿名内部类的写法,替换成lambda的语法就是:
public class LamdbaTest {
public static void main(String[] args) {
// 匿名内部类写法
new Inter() {
@Override
public void show() {
System.out.println("匿名内部类写法");
}
}.show();
// 用lambda的写法
byLambda(() -> System.out.println("采用lambda写法,这就是重写的show的方法体,多个语句则用{}"));
}
public static void byLambda(Inter inter){
inter.show();
}
}
// 接口
interface Inter {
void show();
}
lambda的使用还是有约束的,对比匿名内部类,主要有以下几点区别:
- 所需类型不同。匿名内部类可以是接口、抽象类、具体类。Lambda表达式的参数只能是接口。
- 使用限制不同。如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。 但接口中有多个方法时,lambda的这种写法就不适用了。
- 实现原理不同。匿名内部类虽然跟使用Lambda效果一样,但是会自动多生成一个.class字节码文件,这也是匿名内部类跟Lambda的一大区别。
待补充...