在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