W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
使您的代碼與最新版本的語言和庫保持最新是一項具有挑戰(zhàn)性的任務(wù)。幸運的是,IntelliJ IDEA可以使這變得更容易,通過檢查來指導(dǎo)您的工作,自動修復(fù)和通常的重構(gòu)工具。
Java SE 8為語言帶來了全新的概念,如lambda表達式,并為開發(fā)人員多年來一直使用的類添加了新的方法。此外,還有新的工作方式,包括新的Date和Time API,以及一個可幫助null安全的Optional類型。
在本教程中,我們將展示IntelliJ IDEA如何幫助您將代碼從Java 6(或7)轉(zhuǎn)換為Java 8,并使用代碼示例來顯示可用的幫助以及何時可能或可能不會選擇使用新功能。
本教程假定以下先決條件:
IntelliJ IDEA提供的大量選項和功能可能非常龐大,尤其是在解決與嘗試將整個代碼庫(甚至只是模塊或軟件包)遷移到新版本的問題時。與大多數(shù)軟件開發(fā)問題一樣,以迭代方式處理此問題是值得的。
為此,本教程將對更改進行分組,而不是采用大爆炸的方法。
如果要在CI環(huán)境中編譯代碼,則還需要確保使用Java 8編譯新代碼。
您的項目可能已經(jīng)使用檢查來鼓勵代碼中的某種程度的一致性和質(zhì)量。為了完全專注于進行與升級到Java 8相關(guān)的更改,我們將創(chuàng)建一個新的檢查配置文件。
一旦分析代碼完成運行,您將在檢查結(jié)果(Inspection Results)工具窗口中看到一組結(jié)果。
檢查將顯示您可以自動將代碼轉(zhuǎn)換為使用lambda表達式的位置。在現(xiàn)有代碼中,您可能會發(fā)現(xiàn)許多地方,例如,當您為以下內(nèi)容創(chuàng)建匿名內(nèi)部類時:
Runnable
匿名的內(nèi)部類:
executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { getDs().save(new CappedPic(title)); } }, 0, 500, MILLISECONDS);
executorService.scheduleAtFixedRate(() -> getDs().save(new CappedPic(title)), 0, 500, MILLISECONDS);
您會注意到lambda表達式在類型信息方面的表達非常少。這里,這個lambda代表了Runnable
所有的實現(xiàn), 但卻消失了。IntelliJ IDEA將通過左側(cè)裝訂線中的lambda圖標為您提供有關(guān)lambda表達式類型的信息:
將鼠標懸停在此將告訴您類型,然后單擊可以導(dǎo)航到聲明。
您應(yīng)該能夠自動將此修復(fù)程序應(yīng)用于代碼庫中找到匿名內(nèi)部類的所有位置,而不會影響系統(tǒng)中的功能。應(yīng)用更改通常還會提高代碼的可讀性,刪除上面示例中的樣板行。
但是,您可能需要檢查每個更改,如下所示:
讓我們用一個例子來討論這兩點。
我們可能在我們的測試中使用Runnable對一組特定的斷言進行分組:
Runnable runnable = new Runnable() { @Override public void run() { datastoreProvider.register(database); Assert.assertNull(database.find(User.class, "id", 1).get()); Assert.assertNull(database.find(User.class, "id", 3).get()); User foundUser = database.find(User.class, "id", 2).get(); Assert.assertNotNull(foundUser); Assert.assertNotNull(database.find(User.class, "id", 4).get()); Assert.assertEquals("Should find 1 friend", 1, foundUser.friends.size()); Assert.assertEquals("Should find the right friend", 4, foundUser.friends.get(0).id); } };
將其轉(zhuǎn)換為lambda會導(dǎo)致:
Runnable runnable = () -> { datastoreProvider.register(database); Assert.assertNull(database.find(User.class, "id", 1).get()); Assert.assertNull(database.find(User.class, "id", 3).get()); User foundUser = database.find(User.class, "id", 2).get(); Assert.assertNotNull(foundUser); Assert.assertNotNull(database.find(User.class, "id", 4).get()); Assert.assertEquals("Should find 1 friend", 1, foundUser.friends.size()); Assert.assertEquals("Should find the right friend", 4, foundUser.friends.get(0).id); };
這不會短得多,也不會對可讀性產(chǎn)生太大影響。
在這些情況下,您可以選擇使用IntelliJ IDEA的提取方法將這些行轉(zhuǎn)換為單個方法:
Runnable runnable = () -> { assertUserMatchesSpecification(database, datastoreProvider); };
檢查所有l(wèi)ambda轉(zhuǎn)換的第二個原因是可以進一步簡化一些lambdas。最后一個例子是其中之一 - IntelliJ IDEA將以灰色顯示花括號,并通過Alt+Enter用光標在大括號上按下將彈出建議的更改,語句lambda可以用表達式lambda替換:
接受此更改將導(dǎo)致:
Runnable runnable = () -> assertUserMatchesSpecification(database, datastoreProvider);
一旦您將匿名內(nèi)部類更改為lambdas并進行任何手動調(diào)整,您可能想要進行,例如提取方法或重新格式化代碼,運行所有測試以確保所有內(nèi)容仍然有效。如果是這樣,請將這些更改提交給VCS。一旦你完成了這項工作,你就已經(jīng)準備好進入下一步了。
新的收集方法
Java 8通過Streams API引入了一種處理數(shù)據(jù)集合的新方法。不太為人所知的是,我們習(xí)慣使用的許多Collection
類都有新的方法,而不是通過Streams API來處理的。例如,java.util.Iterable
有一個forEach
方法允許您傳入一個lambda,該lambda表示在每個元素上運行的操作。IntelliJ IDEA的檢查將突出顯示您可以使用此方法和其他新方法的區(qū)域。
for (Class<? extends Annotation > annotation : INTERESTING_ANNOTATIONS) { addAnnotation(annotation); }
INTERESTING_ANNOTATIONS.forEach(this::addAnnotation);
INTERESTING_ANNOTATIONS.forEach((annotation) -> addAnnotation(annotation));
這兩個新表單與原始代碼完全相同 - 對于INTERESTING_ANNOTATIONS
列表中的每個項目 ,它都使用該項目調(diào)用addAnnotation
。
IntelliJ IDEA的檢查將建議在適當?shù)?code>Iterable上使用forEach
,但它也將是新的Streams API,這是一個更好的選擇。
該流API 是用于查詢和操作數(shù)據(jù)的強大工具,并且使用它可以顯著改變和簡化您編寫的代碼。在本教程中,我們將介紹一些最簡單的用例,以幫助您入門。一旦您能更加熟練的使用這種編碼風格,您可能希望進一步使用其功能。
forEach
方法?讓我們來看一個比上一個循環(huán)稍微復(fù)雜的例子:
public void addAllBooksToLibrary(Set<Book> books) { for (Book book: books) { if (book.isInPrint()) { library.add(book); } } }
public void addAllBooksToLibrary(Set <Book> books) { books.stream() .filter(book -> book.isInPrint()) .forEach(library::add); }
forEach
參數(shù)選擇了方法參考。對于過濾器,IntelliJ IDEA使用了lambda,但會在編輯器中建議此特定示例可以使用方法引用:
books.stream() .filter(Book::isInPrint) .forEach(library::add);
你可能會看到“可以替換為collect調(diào)用(can be replaced with collect call)”,而不是“可以替換為 foreach調(diào)用(can be replaced with foreach call)”。這與上面的示例非常相似,但它不是在流的末尾調(diào)用forEach
方法并執(zhí)行某些操作,而是使用流的collect方法將流操作的所有結(jié)果放入新的Collection
操作中。通常會看到 for
循環(huán)遍歷某個集合,執(zhí)行某種過濾或操作,并將結(jié)果輸出到新集合中,這就是此檢查將使用Streams
API識別和遷移的代碼類型。
List <Key> keys = .... List <Key.Id> objIds = new ArrayList<Key.Id>(); for (Key key : keys) { objIds.add(key.getId()); }
List<Key.Id> objIds = keys.stream().map(Key::getId).collect(Collectors.toLis
t());
List<Key.Id> objIds = keys.stream() .map(Key::getId) .collect(Collectors.toList());
Key
,將每個Key
“映射”到其Id
中,并將它們收集到新的列表objIds
中。與forEach
示例一樣 ,如果過濾器需要應(yīng)用于collect語句以及映射,IntelliJ IDEA可以解決,因此它可以巧妙地將許多復(fù)雜循環(huán)轉(zhuǎn)換為一組Stream操作。
運行這些檢查可能會很誘人,而且只需自動地應(yīng)用所有修復(fù)程序即可。在轉(zhuǎn)換代碼以在集合或流上使用新方法時,應(yīng)該稍加注意。IDE將確保您的代碼以與以前相同的方式工作,但您需要在應(yīng)用更改后檢查代碼是否仍然可讀且易于理解。如果您和您的團隊第一次使用Java 8功能,那么一些新代碼將非常陌生并且可能不清楚?;ㄐr間單獨查看每個更改,并確保您在開始之前了解新代碼。
與lambdas一樣,一個好的經(jīng)驗法則是從一小段代碼開始 - 用于轉(zhuǎn)換為兩個或更少的流操作的循環(huán)的縮寫,最好是使用單行l(wèi)ambdas。隨著您對這些方法越來越熟悉,您可能希望解決更復(fù)雜的代碼問題。
我們來看一個例子:
IntelliJ IDEA建議使用下列代碼:
for (Entry<Class <? extends Annotation>, List<Annotation>> e : getAnnotations().entrySet()) { if (e.getValue() != null && !e.getValue().isEmpty()) { for (Annotation annotation: e.getValue()) { destination.addAnnotation(e.getKey(), annotation); } } }
可以轉(zhuǎn)換為這段代碼:
getAnnotations().entrySet() .stream() .filter(e -> e.getValue() != null && !e.getValue().isEmpty()) .forEach(e -> { for (Annotation annotation: e.getValue()) { destination.addAnnotation(e.getKey(), annotation); } });
撇開原始代碼開始時難以理解的事實,您可以選擇不應(yīng)用更改,原因如下:
但是,您可以選擇接受此更改,原因如下:
除了“保持代碼(keep the code)”和“應(yīng)用更改(apply the changes)”選項之外,還有第三種選擇:將舊代碼重構(gòu)為更易讀的內(nèi)容,即使它不使用Java 8。這可能是一段很好的代碼稍后重構(gòu)的一個注釋,而不是試圖解決所有代碼的問題,而只是嘗試采用更多的Java 8約定。
我們?yōu)椤癑ava8”配置文件選擇的檢查幫助我們找到可以使用lambda表達式的位置,Collections上的新方法和Streams API,并將自動應(yīng)用修復(fù)程序到這些位置。Java 8中還有許多其他新功能,在下面的部分中,我們將重點介紹IntelliJ IDEA的一些功能,這些功能也可以幫助您使用它們。
在本節(jié)中,我們將介紹如何使用新的Date和Time API來定位可能會受益的地方,而不是java.util.Date
和java.util.Calendar
。
java.util.Date
了一段時間,但本身并未棄用,因此如果在代碼中使用它,則不會收到棄用警告。這就是為什么這種檢查對于定位用法很有用。Date
字段,例如:
public class HotelBooking { private final Hotel hotel; private final Date checkInDate; private final Date checkOutDate; // constructor, getters and setters... }
LocalDate
替換它,這可以通過上下文菜單中的Refactor|輸入Migration ...(Refactor | Type Migration...)來完成,或者通過快捷鍵:Ctrl+Shift+F6。在彈出窗口中鍵入LocalDate并選擇java.time.LocalDate
。按Enter鍵時,將更改此字段的類型以及getter和setter。您可能仍需要解決使用字段,getter或setter的編譯錯誤。java.time.LocalDateTime
。對于只有時間的字段,java.time.LocalTime
可能是合適的。Date
,則知道這相當于現(xiàn)在的日期和時間:
booking.setCheckInDate(new Date());
now()
方法:
booking.setCheckInDate(LocalDate.now());
java.util.Date
值的常用且可讀的方法是使用java.text.SimpleDateFormat
。您可能會看到類似于以下內(nèi)容的代碼:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); booking.setCheckInDate(format.parse("2017-03-02"));
LocalDate
,您可以輕松地將其設(shè)置為特定日期,而無需使用格式化程序:
booking.setCheckInDate(LocalDate.of(2017, 3, 2));
更新代碼以使用新的Date和Time API比將匿名內(nèi)部類遷移到Lambda Expressions并循環(huán)到Streams API需要更多的手動干預(yù)。IntelliJ IDEA將幫助您了解使用舊java.util.Date和java.util.Calendar類的數(shù)量和位置 ,這將有助于您了解遷移的范圍。IntelliJ IDEA的重構(gòu)工具可以幫助您在必要時遷移這些類型。但是,您需要制定一個策略,了解如何處理每個更改,您要使用哪些新類型,以及如何正確使用這些更改。這不是一個可以自動應(yīng)用的更改。
我們將看到的最后一個Java 8功能是新的Optional類型。java.util.Optional
為您提供了一種處理空值的方法,以及一種指定方法調(diào)用是否應(yīng)返回空值的方法。與日期和時間一樣,IntelliJ IDEA的功能將幫助您識別可能從使用該Optional
類型中受益的代碼區(qū)域 。
Optional
使用中受益的區(qū)域。為簡單起見,我們將考慮僅啟用其中兩項檢查:
Optional
。例如,在下面的代碼中,將標記分配了偏移量的行:
private Integer offset; // code.... public Builder offset(int value) { offset = value > 0 ? value : null; return this; } // more code...
if (offset != null) { cursor.skip(offset); }
Optional
字段更改為Integer
,可以通過:Ctrl+Shift+F6,并更改值的設(shè)置方式:
private Optional<Integer> offset; // code... public Builder offset(int value) { offset = value > 0 ? Optional.of(value) : Optional.empty(); return this; } // more code...
Optional
而不是執(zhí)行空檢查。最簡單的解決方案是:
if (offset.isPresent()) { cursor.skip(offset); }
offset.ifPresent(() -> cursor.skip(offset));
Optional
會使其更加明確,這可能不會返回值。例如,我們的檢查可能會將此方法標記為返回null值:
public Customer findFirst() { if (customers.isEmpty()) { return null; } else { return customers.get(0); } }
Optional
的 Customer
:
public Optional<Customer> findFirst() { if (customers.isEmpty()) { return Optional.empty(); } else { return Optional.ofNullable(customers.get(0)); } }
Optional
類型。如果該值不存在,這可能是決定該怎么做的正確位置。在上面的示例中,調(diào)用findFirst
方法的代碼可能如下所示:
Customer firstCustomer = customerDao.findFirst(); if (firstCustomer == null) { throw new CustomerNotFoundException(); } else { firstCustomer.setNewOffer(offer); }
Optional
,我們可以消除空檢查:
Optional<Customer> firstCustomer = customerDao.findFirst(); firstCustomer.orElseThrow(() -> new CustomerNotFoundException()) .setNewOffer(offer);
將字段類型更改為Optional可能會產(chǎn)生很大影響,并且自動執(zhí)行所有操作并不容易。首先,嘗試繼續(xù)使用Optional類內(nèi)部 - 如果您可以將字段更改為Optional嘗試,而不通過getter和setter公開它,這將允許您進行更漸進的遷移。
將更改方法返回類型更改為Optional會產(chǎn)生更大的影響,您可能會發(fā)現(xiàn)這些更改會以意想不到的方式影響您的代碼庫。將此方法應(yīng)用于可以為null的所有值可能會導(dǎo)致Optional變量和字段遍布整個代碼,多個位置執(zhí)行isPresent檢查或使用Optional方法執(zhí)行操作或拋出適當?shù)漠惓!?/p>
請記住,在Java 8中使用新功能的目的是簡化代碼并提高可讀性,因此將更改的范圍限制為代碼的小部分,并檢查使用Optional是否使代碼更易于理解,而不是更難以實現(xiàn)保持。
IntelliJ IDEA的檢查將確定可能的變更地點,重構(gòu)工具可以幫助應(yīng)用這些變更,但重構(gòu)會對Optional產(chǎn)生很大的影響,您和您的團隊應(yīng)該確定哪些領(lǐng)域需要改變以及如何處理這些變化。您甚至可以使用“Annotate field [fieldName] as @Nullable”的建議修正來標記那些可以遷移到Optional的字段, 以便朝著該方向邁出一步,對代碼的影響更小。
IntelliJ IDEA的檢查,特別是那些圍繞語言遷移的檢查,可以幫助識別代碼中可以重構(gòu)以使用Java 8功能的區(qū)域,甚至可以自動應(yīng)用這些修復(fù)。
如果您已自動應(yīng)用修復(fù)程序,那么查看更新的代碼以檢查它是否難以理解并幫助您熟悉新功能是很有價值的。
本教程提供了有關(guān)如何遷移代碼的一些指示。我們已經(jīng)介紹了lambda表達式和方法引用,一些關(guān)于Collection的新方法 ,介紹了Streams API,展示了IntelliJ IDEA如何幫助您使用新的Date和Time API, 并研究了如何識別使用新的Optional類型可能受益的位置。
Java 8中有許多新功能,旨在使程序員的工作更輕松 - 使代碼更具可讀性,并使其更容易在數(shù)據(jù)結(jié)構(gòu)上執(zhí)行復(fù)雜的操作。IntelliJ IDEA當然不僅支持這些功能,還可以幫助開發(fā)人員使用它們,包括遷移現(xiàn)有代碼并在編輯器中提供幫助和建議,以便在您使用它們時為您提供指導(dǎo)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: