shigen
坚持更新文章的博客写手,记录成长,分享认知,留住感动。个人IP:shigen
提到函数式接口,最常见的就是lambda表达式,IDEA也有智能的提示:
最后改成这样的就是最简洁的、IDEA希望的风格:
List<String> nameList = users.stream().map(User::getName).collect(Collectors.toList());
log.info("nameList:{}", nameList);
直接一行搞定比较复杂的功能。今天要讲到的函数式接口就从这个展开。
1. 函数式接口
所谓的函数式接口指的是只定义一个抽象方法的接口。接口类常用这个注解@FunctionalInterface
表明:
- java.util.Comparator
public interface Comparator<T> {
int compare(T o1, T o2);
}
- java.lang.Runnable
public interface Runnable {
void run();
}
- java.util.concurrent.Callable
public interface Callable<V> {
V call() throws Exception;
}
实现起来基本上是这样的:
(o1, o2) -> o1.getXxx() > o2.getXxx()
() -> void
() -> V类型的对象
其中,callable最常见在多线程上,这里展示下我对于分布式锁的封装:
2. 常用的函数式接口
或许对于Function、Predicate等等你并不陌生,好熟悉、好像在哪里用过就是想不起来!我们还是用stream()流来讲解。前提是构建一个用户列表:
相信截图里User类的属性一目了然。现在我们用stream流来操作。
2.1 Function出现
List<String> nameList = users.stream().map(user -> user.getName()).collect(Collectors.toList());
log.info("nameList:{}", nameList);
这里的逻辑很简单,获得所有的用户名字返回集合。不知道有没有好奇过map,他的参数是什么:
额,就是一个Function
!好的,我这样改造下:
static Function<User, String> nameFunction = user -> user.getName();
public static void main(String[] args) {
List<String> nameList = users.stream().map(nameFunction).collect(Collectors.toList());
log.info("nameList:{}", nameList);
}
其实Function
就是两个类型约束,一个数参数类型,一个是返回值类型,定义了一个固定的逻辑。这个还好,如果稍加对于name的处理,并且是通用的处理方式,就可以考虑用Function写成一个通用的方法。
2.2 Predicate出现
和Function
类似,Predicate
顾名思义,就是断言。可以从stream.filter()中获得:
User user1 = users.stream().filter(user -> "李四".equals(user.getName())).findFirst().orElse(null);
log.info("user1:{}", user1);
对应的改造:
static Predicate<User> namePredicate = user -> "李四".equals(user.getName());
public static void main(String[] args) {
User user1 = users.stream().filter(namePredicate).findFirst().orElse(null);
log.info("user1:{}", user1);
}
Predicate的范型只有一个,就是一个对象,返回的就是断言的方法。
2.3 Cousumer的出现
直接给出代码案例:
Consumer<User> userConsumer = user -> {
String name = user.getName();
log.info("name:{}", name);
};
userConsumer.accept(users.get(0));
适用场景:访问类型T的对象,对其执行某些操作。只有操作,没有返回值,也不需要关注返回值。另外附上我最近看到的一段模板代码:
/**
* 利用设计模式减少样板代码
*/
public static void readLine(String filename, Consumer<String> consumer) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
consumer.accept(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading file", e);
}
}
public static void main(String[] args) {
String filePath = "/Users/shigen/Downloads/area.json";
readLine(filePath, (lines) -> {
System.out.println(lines);
});
}
2.4 Supplier的出现
还是先上代码:
Supplier<User> userSupplier = () -> new User("10005", "shigen", null);
User user2 = userSupplier.get();
log.info("user2:{}", user2);
userConsumer.accept(user2);
这里正好和Consumer相反,这里是生产对象的,然后可以供它消费。
适用场景:定义类型T的对象的生产规则。这里列出一个案例:生成随机数的方法:
Supplier<Integer> randomSupplier = () -> new Random().nextInt(100);
log.info("randomSupplier:{}", randomSupplier.get());
突然感觉曾经写过的工具类可以用这种方式简化一下了。
2.5 Comparator的出现
其实我们用的最多的还是实现Comparator的接口,重写compare方法用来比较对象,多见于需要内存排序的场景:
也可以在stream.sorted()的参数中看出来:
Comparator<User> userComparator =
(user4, user5) -> user5.getName().compareTo(user4.getName());
List<String> sortedName = users.stream().sorted(userComparator).map(User::getId).collect(Collectors.toList());
log.info("sortedName:{}", sortedName);
当然,其它的高级复合Lambda表达式用法,可以参考这篇文章:系统学习lambda表达式。个人认为在业务代码使用复合lambda表达式,会加重代码的理解难度,不推荐。了解常见的函数式接口,并会使用即可。
与shigen一起,每天不一样!