首先假设我们有一个用户类:
import lombok.Data;
/**
* 用户类
*
*/
@Data
public class User {
/**
* 用户 id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 年龄
*/
private Integer age;
/**
* 性别: 0 男 1 女
*/
private Integer gender;
}
假设我们现在需要编写两个方法,一个用于得到列表中所有成年用户,一个需要得到所有的男性用户,一般情况下我们会编写如下的代码(先不考虑 Stream 的方式,同时假设 age 和 gender 都有默认值):
import java.util.ArrayList;
import java.util.List;
/**
* 用户服务类
*
*/
public class UserService {
/**
* 筛选用户列表获取所有的成年用户
*
* @param userList 用户列表
* @return 成年用户列表
*/
public List<User> getAdult(List<User> userList) {
List<User> adultList = new ArrayList<>();
for (User user : userList) {
if (user.getAge() >= 18) {
adultList.add(user);
}
}
return adultList;
}
/**
* 筛选用户列表获取所有的男性用户
*
* @param userList 用户列表
* @return 男性用户列表
*/
public List<User> getMale(List<User> userList) {
List<User> maleList = new ArrayList<>();
for (User user : userList) {
if (user.getGender() == 0) {
maleList.add(user);
}
}
return maleList;
}
}
可以发现这两个方法都是如下的三个步骤:
初始化筛选结果列表。
遍历用户列表,将符合条件的用户添加到结果列表中。
返回筛选结果列表。
如果我们还需要其它的维度对用户列表进行筛选,我们就需要编写很多的重复代码,而只修改其中的筛选条件。而如果我们将筛选条件(类的某个字段满足某个条件,参数为对象,返回值为布尔值)当成普通参数一样传递给方法,我们就可以编写出如下的代码:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* 用户服务类
*
*/
public class UserService {
/**
* 筛选用户列表获取所有的成年用户
*
* @param userList 用户列表
* @return 成年用户列表
*/
public List<User> getAdult(List<User> userList) {
return getUserByCondition(userList, user -> user.getAge() >= 18);
}
/**
* 筛选用户列表获取所有的男性用户
*
* @param userList 用户列表
* @return 男性用户列表
*/
public List<User> getMale(List<User> userList) {
return getUserByCondition(userList, user -> user.getGender() == 0);
}
/**
* 根据条件对用户列表进行筛选
*
* @param userList 用户列表
* @param condition 条件
* @return 筛选结果列表
*/
private List<User> getUserByCondition(List<User> userList, Predicate<User> condition) {
List<User> resultList = new ArrayList<>();
for (User user : userList) {
if (condition.test(user)) {
resultList.add(user);
}
}
return resultList;
}
}
可以发现通过使用Predicate这个函数式接口,我们再编写另外两个方法时,只需要将查询条件传递给getUserByCondition方法接口即可,这也是函数式接口的方便之处。如果我们发现代码中存在大量的重复逻辑,而只有部分执行语句不同时,就可以考虑这部分执行语句是否可以抽离出来通过 Lambada 表达式或者方法引用进行传递,这样就可以避免大量的重复代码。
利用上面的思路,我们就可以写出来一个可以根据条件对所有列表进行筛选的简单 demo 方法:
/**
* 根据条件对列表进行筛选
*
* @param list 列表
* @param condition 条件
* @return 结果列表
*/
public static <E> List<E> filter(List<E> list, Predicate<E> condition) {
List<E> resultList = new ArrayList<>();
for (E e : list) {
if (condition.test(e)) {
resultList.add(e);
}
}
return resultList;
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
filter(list, item -> item > 3).forEach(System.out::println);
}
总结:
而在实际开发中,通过泛型和函数式接口的结合,我们能够精简很多的代码,这里可以参考以若依为例讲解函数式接口的应用,希望上面这个简单的例子能让你对函数式接口的应用有一个大概的认识,后面只需要多加练习,善于发现代码中的优化点,就能够体会到函数式接口的方便之处。