在Java8引入函数式编程以后,不少复杂的代码能够大大的简化,甚至能够简化成一句话。这里就主要经过一个例子,分析使用Lambda表达式、静态方法以及实例方法简化代码的优缺点。express
经过这个例子,一方面能够认识到,本身之前写过的代码可读性是多么的差,另外一方面能够知道如何优雅的使用接口。之前使用接口的时候,只知道实现了这个接口,就表明这个类具备某种能力。可是经过这个例子,我看到了接口还能够用在方法中。实现接口的时候,只须要在lambda表达式中提供对应的(输入/返回值)类型便可。
编程
public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
for (Map.Entry<String, String> entry : map1.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "," + value);
}
for (Map.Entry<String, String> entry : map2.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "," + value);
}
}
public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
for (Map.Entry<String, String> entry : map1.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-" + value);
}
for (Map.Entry<String, String> entry : map2.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-" + value);
}
}
public static void printWithColon(Map<String, String> map1, Map<String, String> map2) {
for (Map.Entry<String, String> entry : map1.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}
for (Map.Entry<String, String> entry : map2.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}
}
复制代码
看完后发现,里面很是多的重复代码,天然而然的想到,能够将其中的公共代码抽取出来。而且函数是输入两个map,获得一个void,这符合BiConsumer接口设定。因此,能够在抽取的公共方法中实现BiConsumer接口,实现代码复用。
代码以下:bash
public static void printWithConsumer(
Map<String, String> map1,
Map<String, String> map2,
BiConsumer<String, String> consumer) {
map1.forEach(consumer);
map2.forEach(consumer);
}
复制代码
打印方法就能够被简化成一句话了:函数式编程
public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
printWithConsumer(map1, map2, (s, s2) -> System.out.println(s+","+s2));
}
复制代码
一样使用第二个例子,对第二种方法进行简化。函数
public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
printWithConsumer(map1, map2, RefactorToConsumer::staticPrintWithDash);
}
public static void staticPrintWithDash(String s1, String s2) {
System.out.println(s1 + "-" + s2);
}
复制代码
定义的静态方法消耗两个String,获得一个void,符合BiCounsumer的约定,因此可使用静态方法引用的方式简化代码。
分析两者优缺点:
1.使用静态方法能够给方法取名字,这样更加直观,更容易被理解。
2.使用静态方法能够在方法中写较为复杂的代码,而Lambda中通常就是一两句话。因此须要较多的代码实现的时候,最好仍是在静态方法中声明。ui
为了便于理解,这里使用另一个简单的例子: 对一个User类实现过滤
代码以下:this
public class User {
private final Integer id;
private final String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
// 过滤姓张的用户
public static List<User> filterZhangUsers(List<User> users) {
List<User> list = new ArrayList<>();
for (User user:users
) {
if (user.name.startsWith("张")){
list.add(user);
}
}
return list;
}
}
复制代码
为了减小排版,我这里只写了一个过滤器,可是能够想象若是须要过滤姓王的,ID为偶数的User...咱们就能够将其抽取出来,造成一个公共的抽取方法。不管用前面的Lambda表达式也好,用静态方法也能够,实现Predicate接口便可。除此以外,咱们还发现这个断定的过程其实就是User类型转换为boolean类型的过程,而刚好方法就是在User类中定义的,因此没必要将方法定义成static类型,使用实例方法便可。
代码以下:spa
public static List<User> filterZhangUsers(List<User> users) {
return filter(users,User::filterNameStartWithZhang);
}
public boolean filterNameStartWithZhang(){
return this.name.startsWith("张");
}
public static List<User> filter(List<User> users, Predicate<User> predicate) {
List<User> results = new ArrayList<>();
for (User user : users
) {
if (predicate.test(user)) {
results.add(user);
}
}
return results;
}
复制代码
因为 filterNameStartWithZhang()方法是非静态的,在方法形参表中看似是空的,实际上形参是(User this),符合Predicate的从某一类型到boolean的设定。
code
总结:
1.将大量的重复代码进行重构,是一件很是有必要的事情。不只有形的减小代码量,并且在无形中减小了出bug的机率,而且大大的增长了代码的可读性。
2.使用Lambda表达式须要注意,在表达式中引用的变量都须要是effectively final类型的,不然会报 “Variable used in lambda expression should be final or effectively final”。
接口
解决方法:
1.可使用Atomic类型的参数。2.能够将参数声明为全局类型的。
详细参考这篇文章:zhuanlan.zhihu.com/p/82921974