Java 8函數語言程式設計模式:不要使用匿名函式
本文將引導你完成一系列從傳統的命令式程式碼重構到Java 8函式程式碼,要從本文中獲得最大收益,你應該具備Java 8函式的一些實踐經驗。
1)優先於匿名Lambda的命名函式
為了熱身,讓我們從簡單的任務開始,將一些使用者的詳細資訊帶到UI。我們將從實體列表的開始,將User 轉換到 UserDto:
<b>public</b> List<UserDto> getAllUsers() { List<User> users = userRepo.findAll(); List<UserDto> dtos = <b>new</b> ArrayList<>(); <b>for</b> (User user : users) { UserDto dto = <b>new</b> UserDto(); dto.setUsername(user.getUsername()); dto.setFullName(user.getFirstName() + <font>" "</font><font> + user.getLastName().toUpperCase()); dto.setActive(user.getDeactivationDate() == <b>null</b>); dtos.add(dto); } <b>return</b> dtos; } </font>
但是,我對這段程式碼並不感到自豪,因為我很可能會為許多用例重複編寫類似的程式碼。那麼,讓我們使用Java 8:
<b>public</b> List<UserDto> getAllUsers() { <b>return</b> userRepo.findAll().stream() .map(user -> { UserDto dto = <b>new</b> UserDto(); dto.setUsername(user.getUsername()); dto.setFullName(user.getFirstName() + <font>" "</font><font> + user.getLastName().toUpperCase()); dto.setActive(user.getDeactivationDate() == <b>null</b>); <b>return</b> dto; }) .collect(toList()); </font>
雖然不錯,但是,我仍然不滿意。我寫的這個lambda演示了一個“匿名函式”。作為一個乾淨的程式碼瘋子,我有一個問題 - 我想要富有表現力的名字。所以,我很快將lambda內容提取到一個單獨的方法中:
<b>public</b> List<UserDto> getAllUsers() { <b>return</b> userRepo.findAll().stream().map(<b>this</b>::toDto).collect(toList()); } <b>private</b> UserDto toDto(User user) { UserDto dto = <b>new</b> UserDto(); dto.setUsername(user.getUsername()); dto.setFullName(user.getFirstName() + <font>" "</font><font> + user.getLastName().toUpperCase()); dto.setActive(user.getDeactivationDate() == <b>null</b>); <b>return</b> dto; } </font>
程式碼比在之前的版本中更簡單,但現在它稍微好一些。
這種User到DTO的邏輯轉換可以直接放在DTO建構函式中:
<b>public</b> <b>class</b> UserFacade { <b>private</b> UserRepo userRepo; <b>public</b> List<UserDto> getAllUsers() { <b>return</b> userRepo.findAll().stream().map(UserDto::<b>new</b>).collect(toList()); } } <b>public</b> <b>class</b> UserDto { <b>private</b> String username; <b>private</b> String fullName; <b>private</b> <b>boolean</b> active; <b>public</b> UserDto(User user) { username = user.getUsername(); fullName = user.getFirstName() + <font>" "</font><font> + user.getLastName().toUpperCase(); active = user.getDeactivationDate() == <b>null</b>; } ... } </font>
現在,讓我們假設這個轉換需要一些其他元件的幫助,我們希望使用Spring,Guice,CDI等注入。但是,在我們例項化的類中注入依賴項需要非常複雜的程式碼。如果這個轉換過於複雜,我們應該將它移到一個單獨的UserMapper類並從那裡引用它:
@Service <b>public</b> <b>class</b> UserFacade { @Autowired <b>private</b> UserRepo userRepo; @Autowired <b>private</b> UserMapper mapper; <b>public</b> List<UserDto> getAllUsers() { <b>return</b> userRepo.findAll().stream().map(mapper::toDto).collect(toList()); } } @Component <b>public</b> <b>class</b> UserMapper { @Autowired <b>private</b> OtherClass otherClass; <b>public</b> UserDto toDto(User user) { UserDto dto = <b>new</b> UserDto(); dto.setUsername(user.getUsername()); ... <font><i>// code using otherClass</i></font><font> <b>return</b> dto; } } </font>
關鍵點是:始終將複雜的lambda提取到具有表達名稱的函式中,然後可以使用以下四點(::)來引用:
- 如在同一個類,使用this::;
- 在另外一個類似於(mapper::);
- 一些靜態助手方法(SomeClass::);
- Stream 中條目型別(Item::);
- 甚至一些建構函式(),如果它足夠簡單;UserDto::new
記住,不要使用匿名型別。
在ofollow,noindex" target="_blank">這個GitHub儲存庫中 提交了練習的每個階段,所以請隨意瀏覽儲存庫以檢視所有內容。