关于java8新特性lambda应用场景之函数式接口的理解
lambda是java8的新特性,关于lambda的的应用场景官方解释中有一条是这样的 “任何有函数式接口的地方” ,今天就捋一下这是个什么东西
当我们有一个学生类,
@Data @AllArgsConstructor public class Student { private String name; private Integer age; private Integer score; }
我们现在想通过Student的某个属性来过滤筛选元素,例:1.筛选年龄大于20的;2筛选分数大于79的,我们正常会这么写:
public class Test { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("andy", 24, 80)); students.add(new Student("rofin", 21, 70)); students.add(new Student("jesse", 18, 95)); students.add(new Student("george", 25, 75)); filterByAge(students); System.out.println("---------------"); filterByScore(students); } private static void filterByScore(List<Student> students) { List<Student> filterStudents = new ArrayList<>(); for (Student s : students) { if (s.getScore() > 79) { filterStudents.add(s); } } printStudent(filterStudents); } private static void filterByAge(List<Student> students) { List<Student> filterStudents = new ArrayList<>(); for (Student s : students) { if (s.getAge() > 20) { filterStudents.add(s); } } printStudent(filterStudents); } private static void printStudent(List<Student> students) { students.forEach(System.out::println); } }
但是我们发现filterByScore与filterByAge方法体及其相似,或许以后,我们可能还会有根据name属性来做一些筛选的需求,而且我们这里的Student是一个很基础的对象,属性简单,当我们遇到属性较复杂的对象时,难道要每次都要改代码来实现这些过滤方法吗?此时我们有了重构的想法。
我们把过滤的这个动作抽象出来,单独写一个接口,如下:
public interface StudentFilter { /** * 提供一个过滤学生的接口方法 * @param student * @param condition * @return */ boolean filterStudents(Student student, Object condition); }
然后由具体的实现类负责实现具体的比较方法
public class StudentScoreFilter implements StudentFilter { /** * 过滤掉小于指定条件的元素 * @param student * @param condition * @return */ @Override public boolean filterStudents(Student student, Object condition) { return student.getScore()>(Integer) condition; } } public class StudentAgeFilter implements StudentFilter { /** * 过滤掉小于指定条件的元素 * @param student * @param condition * @return */ @Override public boolean filterStudents( Student student, Object condition) { return student.getAge()>(Integer) condition; } }
有了上面的接口与实现类我们可以再包装一个方法
/** * 通过入参来表达调用方是需要哪种过滤器 * @param students * @param studentFilter * @param condition * @return */ private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter,Object condition) { ArrayList<Student> list = new ArrayList<>(); for (Student s : students) { if (studentFilter.filterStudents(s,condition)){ list.add(s); } } return list; }
此时,外部调用在调用时,只需要通过传不同的参数就能实现对student集合做不同方式的筛选(姑且认为是策略模式吧…………),看一下外部调用
public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("andy", 24, 80)); students.add(new Student("rofin", 21, 70)); students.add(new Student("jesse", 18, 95)); students.add(new Student("george", 25, 75)); printStudent(getByFilter(students, new StudentAgeFilter(), 20)); System.out.println("---------------"); printStudent(getByFilter(students, new StudentScoreFilter(), 79)); } private static void printStudent(List<Student> students) { students.forEach(System.out::println); }
到这,我们似乎解决了扩展性的问题。大多时候,的确如此,可惜,我们今天的主角不是它。考虑一下,我们如果现在想通过name的长度大于4来筛选元素,需要加一个StudentNameFilter类实现StudentFilter然后写具体的筛选逻辑,才能达到我们的目的。综合上面的需求一起看,好像定义的这些实现类只干了一件事,就是一个简单的比较,那么是不是可以把这些实现转移到调用的地方来实现?如下使用匿名内部类来实现:
public class Test { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("andy", 24, 80)); students.add(new Student("rofin", 21, 70)); students.add(new Student("jesse", 18, 95)); students.add(new Student("george", 25, 75)); //内部类的表达方式 List<Student> students1 = getByFilter(students, new StudentFilter() { @Override public boolean filterStudents(Student student, Object condition) { return student.getAge()>(Integer) condition; } }, 20); List<Student> students2 = getByFilter(students, new StudentFilter() { @Override public boolean filterStudents(Student student, Object condition) { return student.getScore()>(Integer) condition; } }, 70); printStudent(students1); System.out.println("------------"); printStudent(students2); } /** * 通过入参来表达调用方是需要哪种过滤器 * @param students * @param studentFilter * @param condition * @return */ private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter, Object condition) { ArrayList<Student> list = new ArrayList<>(); for (Student s : students) { if (studentFilter.filterStudents(s,condition)){ list.add(s); } } return list; } private static void printStudent(List<Student> students) { students.forEach(System.out::println); } }
既然,使用了内部类,内部类的写法,好像可以使用lambda的方式来表达:
public class Test { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("andy", 24, 80)); students.add(new Student("rofin", 21, 70)); students.add(new Student("jesse", 18, 95)); students.add(new Student("george", 25, 75)); //使用lambda代替内部类 List<Student> students1 = getByFilter(students, ((student, condition) -> { return student.getAge() > (Integer) condition; }), 20); printStudent(students1); System.out.println("------------"); printStudent(getByFilter(students, (student, condition) -> (student.getScore() > ((Integer) condition)), 70)); System.out.println("------------"); //当想使用student的name属性长度进行过滤时不需要再实现一个StudentNameFilter 直接使用lambda表达式即可。 // 即释义了 任何有函数式接口的地方都可以使用lambda printStudent(getByFilter(students, (student, condition) -> { return student.getName().length() > (Integer) condition; }, 4)); } /** * 通过入参来表达调用方是需要哪种过滤器 * * @param students * @param studentFilter * @param condition * @return */ private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter, Object condition) { ArrayList<Student> list = new ArrayList<>(); for (Student s : students) { if (studentFilter.filterStudents(s, condition)) { list.add(s); } } return list; } private static void printStudent(List<Student> students) { students.forEach(System.out::println); } }
至此 我们的基本可以得出的结论:
任何有函数式接口的地方都可以使用lambda表达式,那什么又是函数式接口呢?就是只有一个抽象方法(Object类中方法除外)的接口是函数式接口。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术