0
点赞
收藏
分享

微信扫一扫

我使用 Lambda 表达式将上百行的代码精简到几行


系列文章

序号

标题

文章链接

1

玩转 Java8 Stream,让你代码更高效紧凑简洁

2

玩转 Java8 Optional,让你代码更紧凑简洁且不再出现空指针


目录


系列文章一、Lambda 简介二、Lambda 优势
  • 2.1 案例一
  • 2.2 案例二
  • 三、Lambda 语法

一、Lambda 简介


​Lambda​​ 表达式,也可称为​​闭包​​,是一个​​匿名函数​​。我们可以把 Lambda 表达式理解为是​​一段可传递的代码​​(像数据一样传递)。即 Lambda 允许把​​函数​​作为一个方法的​​实参参数​​(函数当作参数传递到方法中)。是​​Java8​​的其中一个很重要的新特性。

从而可以写出更简洁,更灵活的代码。作为一种更加紧凑的代码风格,使java的语言表达能力得到了提升。


public class LambdaDemo {
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
//x -> System.out.println(x),就是一个匿名函数,即Lambda表达式,作为实参传给dealList方法
dealList(list, x -> System.out.println(x));
}

public static void dealList(List<String> list, Consumer<String> consumer) {
// 遍历list中的每一个元素,传给consumer对象的accept函数,进行调用
for (String x : list) {
consumer.accept(x);
}
}
}




Lambda 表达式是对某些接口的简单实现,但不是所有接口都可以使用 Lambda 表达式来实现的,Lambda 规定能被 Lambda 表达式实现的接口中,它​​只能只有一个需要被实现的方法(函数)​​​,但​​不要求接口中只能只有一个方法。​​​因为Java8中有另外一个新特性,即 ​​default​​​ 关键字修饰的接口方法有默认实现,这个默认的方法是可以不需要子类实现的,可使用​​@FunctionalInterface​​注解来强制使接口只能有一个需要被实现的方法。


// 此注解表明此接口为函数接口,即只能有一个抽象方法
@FunctionalInterface
public interface Human {

// 抽象方法,需要被实现
void eat(String name);

// default修饰的默认方法,不需要被子类实现
default void run() {
System.out.println("I can run...");
}
}
public static void main(String[] args) {
Human human = x -> System.out.println(x + " is eat");
human.eat("Mr.nobody");
human.run();
}
// 输出结果
Mr.nobody is eat
I can run...



二、Lambda 优势

2.1 案例一


以前我们要写一个类才能实现一个接口,并且实现里面的抽象方法。如方式一:


// 接口
public interface Human {
// 抽象方法,需要被实现
void speak();
}
// 实现类,并且实现抽象方法
public class Man implements Human {
@Override
public void speak() {
System.out.println("I am man!");
}
}
public class Main {
public static void main(String[] args) {
// 方式一 直接编写实现类
Human human = new Man();
human.speak();
}
}




如果我们不想编写一个单独的实现类(Man),则可以用匿名内部类。如方式二:


public class Main {
public static void main(String[] args) {
// 方式一 直接编写实现类
Human human = new Man();
human.speak();

// 方式二 匿名内部类
Human human1 = new Human() {
@Override
public void speak() {
System.out.println("I am woman!");
}
};
human1.speak();
}
}




但方式二中,有用的就只有​​System.out.println("I am woman!");​​这一行,所以有了Lambda表达式,可以这样写,如方式三:


public class Main {
public static void main(String[] args) {
// 方式一 直接编写实现类
Human human = new Man();
human.speak();

// 方式二 匿名内部类
Human human1 = new Human() {
@Override
public void speak() {
System.out.println("I am woman!");
}
};
human1.speak();

// 方式三 Lambda表达式
Human human2 = () -> System.out.println("I am woman!");
human2.speak();
}
}



2.2 案例二


再假如我们要对一个Student类的数组按指定条件进行过滤,如下:


public class Student {
private String name;
private int age;
private double score;
}
public class Main {

public static void main(String[] args) {

List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

// 过滤出年龄大于等于20的学生
List<Student> stus1 = filterStudentByAge(students);
System.out.println(stus1);

// 过滤出成绩大于80的学生
List<Student> stus2 = filterStudentByScore(students);
System.out.println(stus2);
}

// 过滤出年龄大于等于20的学生
private static List<Student> filterStudentByAge(List<Student> students) {
List<Student> stus = new ArrayList<>();
for (Student stu : students) {
if (stu.getAge() >= 20) {
stus.add(stu);
}
}
return stus;
}

// 过滤出成绩大于80的学生
private static List<Student> filterStudentByScore(List<Student> students) {
List<Student> stus = new ArrayList<>();
for (Student stu : students) {
if (stu.getScore() > 80) {
stus.add(stu);
}
}
return stus;
}
}




按上面的方式,如果要按另外一个条件过滤呢,又要写一个方法。那可以用策略模式处理,编写一个抽象策略接口,然后编写多个不同策略类实现它。


// 策略接口
public interface MyPredicate<T> {
boolean test(T t);
}
// 过滤出年龄大于等于20的学生
public class filterStudentByAge implements MyPredicate<Student> {
@Override
public boolean test(Student t) {
return t.getAge() >= 20;
}
}
// 过滤出成绩大于80的学生
public class filterStudentByScore implements MyPredicate<Student> {
@Override
public boolean test(Student t) {
return t.getScore() > 80;
}
}
public class Main {

public static void main(String[] args) {

List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

// 过滤出年龄大于等于20的学生
List<Student> stus1 = filterStudent(students, new FilterStudentByAge());
System.out.println(stus1);

// 过滤出成绩大于80的学生
List<Student> stus2 = filterStudent(students, new FilterStudentByScore());
System.out.println(stus2);
}

// 按myPredicate策略过滤出满足条件的学生
private static List<Student> filterStudent(List<Student> students,
MyPredicate<Student> myPredicate) {
List<Student> stus = new ArrayList<>();
for (Student stu : students) {
if (myPredicate.test(stu)) {
stus.add(stu);
}
}
return stus;
}
}




但是以上方式,每增加一个过滤条件,就要编写一个策略类,太麻烦。所以我们就用匿名内部类方式。


// 匿名内部类形式 过滤出年龄大于等于18的学生
List<Student> stus3 = filterStudent(students, new MyPredicate<Student>() {
@Override
public boolean test(Student t) {
return t.getAge() > 18;
}
});
System.out.println(stus3);




但我们会觉得匿名内部类还是太麻烦,无用代码太多,有用的代码其实就只有​​return t.getAge() > 18;​​,于是 Lambda 表达式发挥的作用就来了:


// Lambda形式 过滤出年龄大于等于18的学生
List<Student> stus4 = filterStudent(students, t -> t.getAge() > 18);
System.out.println(stus4);




这时还是有人会问,那我们定义的​​接口MyPredicate​​​和​​方法filterStudent()​​​,好像没什么作用呀。然而官方已经想到这一点,它内置了一些通用接口,我们可以使用它。例如断言的接口 ​​Predicate​​​,那我们就用如下方式,完全不用写​​接口MyPredicate​​​和​​方法filterStudent()​​,如下:


// 按myPredicate策略过滤出满足条件的学生
private static List<Student> filterStudent(List<Student> students,
Predicate<Student> predicate) {
List<Student> stus = new ArrayList<>();
for (Student stu : students) {
if (predicate.test(stu)) {
stus.add(stu);
}
}
return stus;
}


当然,如果你会使用Stream(可以看我另外一篇文章),只需要写下面的代码,如下:


public class Main {

public static void main(String[] args) {

List<Student> students = Arrays.asList(new Student("张三", 18, 89.5),
new Student("李四", 20, 60), new Student("王五", 19, 100), new Student("赵六", 22, 89));

// 过滤出年龄大于等于20的学生
students.stream().filter(t -> t.getAge() >= 20).forEach(System.out::println);

System.out.println("-------------------------------------");

// 过滤出成绩大于80的学生
students.stream().filter(t -> t.getScore() > 80).forEach(System.out::println);
}
}

三、Lambda 语法


语法:() -> {}
():Lambda的形参列表,也就是接口里面那个抽象方法的形参列表。
->:Lambda的操作符,可以理解为参数和Lambda体的分隔符。
{}:实现了接口中的抽象方法的方法体。



我们还是以一个简单的例子,由浅到深学习 Lambda 语法。按照语法,我们可以写出如下 Lambda 表达式,​​(String name, int age)​​​ 是参数列表,​​->​​​ 是分隔符,​​{}​​ 中的代码是方法体。


// 函数接口
@FunctionalInterface
public interface Human {
// 抽象方法,需要被实现
String speak(String name, int age);
}
public class LambdaDemo {
public static void main(String[] args) {
Human human = (String name, int age) -> {
System.out.println("My name is " + name + " ,I am " + age + " years old.");
return name;
};
human.speak("Mr.nobody", 18);
}
}


当然,​​()​​​ 括号内的​​参数类型还能省略​​(推荐)。


public class LambdaDemo {
public static void main(String[] args) {
Human human = (name, age) -> {
System.out.println("My name is " + name + " ,I am " + age + " years old.");
return name;
};
human.speak("Mr.nobody", 18);
}
}


如果是​​只有一个参数,() 也能省略​​。


@FunctionalInterface
public interface Human {
// 抽象方法,需要被实现
String speak(String name);
}
public class LambdaDemo {
public static void main(String[] args) {
Human human = name -> {
System.out.println("My name is " + name + ".");
return name;
};
human.speak("Mr.nobody");
}
}


如果,方法体 ​​{}​​ 中,只有一行语句,{} 也能省略(推荐)。


@FunctionalInterface
public interface Human {
// 抽象方法,需要被实现
void speak(String name, int age);
}
public class LambdaDemo {
public static void main(String[] args) {
Human human = (name, age) -> System.out
.println("My name is " + name + " ,I am " + age + " years old.");
human.speak("Mr.nobody", 18);
}
}


如果​​方法体需要返回值​​​,而且​​只有一行语句​​​,那 ​​{}​​​ 大括号和 ​​return​​ 关键字都可以省略(推荐)。


@FunctionalInterface
public interface Human {
// 抽象方法,需要被实现
String speak(String name, int age);
}
public class LambdaDemo {
public static void main(String[] args) {
Human human = (name, age) -> "My name is " + name + " ,I am " + age + " years old.";
human.speak("Mr.nobody", 18);
}
}



举报

相关推荐

0 条评论