Lambda表达式的介绍
Lambda表达式是Java8中最重要的新功能之一.使用Lambda表达式可以代替只有一个抽象函数的接口实现,告别匿名内部类,代码看起来更简洁易懂.Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作
我们接下来看一下创建新线程匿名内部类的写法
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running1........");
}
});
thread.start();
我们使用Lambda建立新线程类的写法
new Thread(()->{System.out.println("running2........");}).start();
很明显,Lambda可以很大程度上降低我们的代码量
我们接下来使用匿名内部类的方法来对数组进行排序
List<String> list = Arrays.asList("java","javaScript","scala","python");
Collections.sort(list,new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
for (String str:list){
System.out.println(str);
}
我们使用lambda表达式来对数组进行排序
List<String> list = Arrays.asList("java","javaScript","scala","python");
Collections.sort(list,(a,b)->a.length()-b.length());
list.forEach(System.out::println);
Lambda表达式的特点
- 函数式编程
- 参数类型自动推断
- 代码量少、简洁
Lambda表达式应用场景
任何有 函数式接口 的地方
我们接下来做一个学生筛选的练习
首先先建立一个学生类
public class Student {
private String name;
private int age;
private int score;
public Student(){
}
public Student(String name,int age,int score){
this.name = name;
this.age = age;
this.score = score;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setScore(int score) {
this.score = score;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getScore() {
return score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", score=" + score +
'}';
}
}
然后可以通过以上方式对学生进行筛选
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan",14,67));
list.add(new Student("lisi",13,89));
list.add(new Student("wangwu",15,99));
list.add(new Student("maliu",9,56));
list.add(new Student("zhaoqi",57,41));
//查找年龄大于14的学生
findByAge(list);
System.out.println("-----------------------------------------------------------------");
//查找分数大于75的学生
findByScore(list);
}
public static void findByAge(ArrayList<Student> students){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (student.getAge()>14){
list.add(student);
}
}
for (Student student:list){
System.out.println(student.toString());
}
}
public static void findByScore(ArrayList<Student> students){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (student.getScore()>75){
list.add(student);
}
}
for (Student student:list){
System.out.println(student.toString());
}
}
}
上图是我们普遍使用的方法,但是可以发现findByAge和findByScore两个方法里面的代码几乎一摸一样,我们该如何进行优化呢?,我们也可以创建一个Filter的接口来进行代码重构,来专门针对Student进行排序
首先我们看创建一个Filter的接口
public interface StudentFilter {
boolean compare(Student student);
}
然后对于Age和Score的筛选,我们可以分别建立两个类来实现Filter接口
public class AgeFilter implements StudentFilter {
@Override
public boolean compare(Student student) {
return student.getAge()>14;
}
}
public class ScoreFilter implements StudentFilter {
@Override
public boolean compare(Student student) {
return student.getScore()>75;
}
}
然后编写方法调用上面的方法传入列表和上面的接口对象进行筛选
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan",14,67));
list.add(new Student("lisi",13,89));
list.add(new Student("wangwu",15,99));
list.add(new Student("maliu",9,56));
list.add(new Student("zhaoqi",57,41));
//查找年龄大于14的学生
getByFilter(list,new AgeFilter());
System.out.println("-----------------------------------------------------------------");
//查找分数大于75的学生
getByFilter(list,new ScoreFilter());
}
public static void getByFilter(ArrayList<Student> students,StudentFilter filter){
ArrayList<Student> list = new ArrayList<Student>();
for (Student student:students){
if (filter.compare(student)){
list.add(student);
}
}
printStuent(list);
}
public static void printStuent(ArrayList<Student> students){
for (Student student:students){
System.out.println(student);
}
}
}
这样通过接口可以是我们的重复代码变得更少,而且代码的拓展性也会比以前强上不少.
如果需要新加不同的筛选方式,若我们新的筛选方式在后期复用次数比较多的话,新建一个类是比较好的方式,如果只是一次性使用,我们可以使用匿名内部类的方式来进行编写
getByFilter(list, new StudentFilter() {
@Override
public boolean compare(Student student) {
return student.getName().length()<6;
}
});
同样我们也可以使用Lambda表达式来达到匿名内部类的效果
getByFilter(list,(e)->e.getName().length()<6);
什么时候适合写Lambda表达式:
任何有函数式接口的地方都要使用Lambda表达式
什么是函数式接口:
只有一个 抽象方法(Object类中的方法除外)的接口是函数式接口
可使用@FunctionalInterface注解来检测
@FunctionalInterface
public interface StudentFilter {
boolean compare(Student student);
}
函数式接口
类型 | 说明 |
---|---|
Supplier | 代表一个输出 |
Consumer | 代表一个输入 |
BiConsumer | 代表两个输入 |
Function | 代表一个输入,一个输出(一般输入和输出是不同类型的) |
UnaryOperator | 代表一个输入,一个输出(输入和输出是相同类型的) |
BiFunction | 代表两个输入,一个输出(输入和输出是不同类型的) |
BinaryOperator | 代表两个输入,一个输出(输入和输出是相同类型的) |
java中提供了一系列的函数式接口,用来接收后续传入的逻辑,但是对输入输出有要求
我们使用Runnable来尝试下我们的Lambda表达式
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("running1........");
}
};
runnable.run();
Runnable runnable2 = ()->{
System.out.println("running2......");
};
runnable2.run();
Runnable runnable3 = ()->System.out.println("running3........");
runnable3.run();
三种表达方式其实效果是一样的
接下来我们使用Callable来尝试下我们的Lambda表达式
Callable<String> callable1 = new Callable<String>() {
@Override
public String call() throws Exception {
return "hahahha";
}
};
System.out.println(callable1.call());
Callable<String> callable2 = ()->{return "hahaha222";};
System.out.println(callable2.call());
Callable callable3 = ()->"hahahah33333";
System.out.println(callable3.call());
上面三种表达方式其实效果也是一样的
上面分别表现的是没有返回值的和又返回值的lambda表达式
我们也可以自己编写函数式接口,接下来我们使用自己编写的函数式接口来设置有输入参数无返回值的Lambda
首先我们来编写一个函数式接口
@FunctionalInterface
public interface StudentDao {
public void insert(Student student);
}
接下来我们来编写一个Student类
public class Student {
String name;
int age;
public Student(){}
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Student类里面我们设置了传参和不穿参的构造方法
接下来我们调用我们自己编写的函数式接口
public class TestTest {
public static void main(String[] args) {
StudentDao studentDao = new StudentDao() {
@Override
public void insert(Student student) {
System.out.println("插入学生");
}
} ;
studentDao.insert(new Student());
StudentDao studentDao2 = (Student student)->{System.out.println("Studeng: "+student.toString());};
studentDao2.insert(new Student("lihua",22));
StudentDao studentDao3 = (Student student)->System.out.println("Student:"+student.toString());
studentDao3.insert(new Student("王五",13));
}
}
StudentDao studentDao3 = (Student student)->System.out.println("Student:"+student.toString());
StudentDao studentDao4 = (student)->System.out.println("Student:"+student.toString());
这里面的两种传参方式其实都是可以的,效果一样
我们也可在lambda里面调用其他方法,编写多行的代码逻辑:
public class TestTest {
public static void main(String[] args) {
StudentDao studentDao4 = student -> {int a = upAge();
student.age = student.age+a;
System.out.println("student: "+student.toString());
};
studentDao4.insert(new Student("刘八",12));
}
static int upAge(){
return 10;
}
}