Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Java 8
Code

適用於Android開發的Java 8:默認和靜態方法

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Java 8 for Android Development.
Java 8 for Android: Cleaner Code With Lambda Expressions
Java 8 for Android Development: Stream API and Date & Time Libraries

Chinese (Traditional) (中文(繁體)) translation by Zhang Xiang Liang (you can also view the original English article)

Java 8是編程語言向前邁進的壹大步,現在隨著Android Studio 3.0的發布,Android開發人員終於可以獲得對Java 8最重要特性的內置支持。 

在這個由三部分組成的系列中,我們已經探索了Java 8的功能,妳可以開始使用妳的Android項目今天。在Cleaner代碼中使用Lambda表達式,在深入研究lambda表達式之前,我們將開發設置為使用Android默認工具鏈提供的Java 8支持。 

在這篇文章中,我們將介紹兩種不同的方式,您可以在接口中聲明非抽象方法(這在早期版本的Java中是不可行的)。我們還將回答現在接口可以實現方法的問題,抽象類和接口之間究竟有什麽區別? 我們還將介紹Java 8功能,該功能可讓您自由地在同壹位置使用相同的註釋,並保持向後兼容早期版本的Android。 

但首先,我們來看壹下Java 8特性,該特性旨在與我們在前壹篇文章中看到的lambda表達式結合使用  。 

.

使用方法參考編寫Cleaner Lambda表達式

在上壹篇文章中,您看到了如何使用lambda表達式從Android應用程序中刪除大量樣板代碼。但是,當lambda表達式僅調用壹個已有名稱的單個方法時,可以使用方法引用來減少項目中的更多代碼。 

例如,這個lambda表達式實際上只是將工作重定向到現有handleViewClick方法:

在這種情況下,我們可以使用::方法引用操作符通過名稱引用此方法。您可以使用以下格式創建這種方法參考: 

在我們的Floating Action Button示例中,我們可以使用方法引用作為lambda表達式的主體:

請註意,被引用的方法必須采用與接口相同的參數 - 在這種情況下,就是View。

您可以使用方法引用運算符(::)來引用以下任何壹項:

靜態方法

如果妳有壹個調用靜態方法的lambda表達式:

然後妳可以把它變成壹個方法參考:

例如,如果妳PrintMessage在壹個MyClass類中有壹個靜態方法,那麽妳的方法引用看起來像這樣:

壹個特定對象的實例方法

這是壹個事先知道的對象的實例方法,允許您替換:

附:

所以,如果妳有以下lambda表達式:

然後引入壹個方法引用會給妳以下內容:

壹種特定類型任意對象的實例方法

這是稍後將提供的任意對象的實例方法,並以以下格式寫入:

構造函數引用

除了使用關鍵字new來調用構造函數外,構造函數的引用與方法引用類似。例如,Button::new是該類的構造函數引用Button,但調用的確切構造函數取決於上下文。 

使用構造函數引用,妳可以打開:

成:

例如,如果您有以下MyInterface界面:

然後妳可以使用構造函數引用來創建新的Student實例:

也可以為數組類型創建構造函數引用。例如,壹個ints 數組的構造函數引用  是int[]::new

將默認方法添加到您的接口

在Java 8之前,只能在接口中包含抽象方法(即沒有正文的方法),這使得難以在接口發布後發布。

每次向接口定義添加方法時,實現此接口的任何類都會突然丟失實現。例如,如果妳有壹個MyInterface被使用過的接口(MyClass),然後添加壹個方法來MyInterface破壞兼容性MyClass。 

在最好的情況下,妳負責使用少量的類MyInterface,這種行為會令人討厭但易於管理 - 妳只需要留出壹些時間來用新的實現更新妳的類。然而,事情可能會變得很大,如果實施了大量的類更復雜MyInterface,或者如果界面中,妳是不負責的班級使用。 

雖然這個問題有很多解決方法,但都不是理想的。例如,您可以在抽象類中包含新方法,但這仍然需要每個人都更新其代碼以擴展此抽象類; 而且,盡管可以用新界面擴展原始界面,但任何想要使用這些新方法的人都需要重寫所有現有界面引用。 

通過在Java 8中引入默認方法,現在可以在接口中聲明非抽象方法(即帶有主體的方法),以便最終為您的方法創建默認實現。 

當您將方法作為默認方法添加到接口時,實現此接口的任何類都不壹定需要提供它自己的實現,這使您可以在不中斷兼容性的情況下更新接口。如果您將新方法作為默認方法添加到接口中,那麽使用此接口但不提供其自己實現的每個類都將簡單地繼承該方法的默認實現。由於類不缺少實現,它將繼續正常工作。 實際上,默認方法的引入是Oracle能夠在Java 8中對Collections API進行如此大量的添加的原因。  實際上,默認方法的引入是Oracle能夠在Java 8中對Collections API進行如此大量的添加的原因。

Collection是壹個在許多不同類中使用的通用接口,因此向該接口添加新方法有可能破壞無數代碼行。

與其向Collection接口添加新方法並打破從此接口派生的每個類,Oracle創建了默認方法功能,然後將這些新方法作為默認方法添加。如果妳看看新的Collection.Stream()方法(我們將在第三部分詳細探討),妳會發現它被添加為默認方法:  如果妳看看新的Collection.Stream()方法(我們將在第三部分詳細探討),妳會發現它被添加為默認方法: 

創建壹個默認方法很簡單 - 只需將default修飾符添加到方法簽名中即可:

現在,如果MyClass使用MyInterface但不提供它自己的實現defaultMethod,它只會繼承由提供的默認實現MyInterface。例如,以下類仍將編譯: 

實現類可以覆蓋接口提供的默認實現,所以類仍然完全控制它們的實現。

雖然缺省方法對於API設計者來說是壹個受歡迎的補充,但它們偶爾會為試圖在同壹個類中使用多個接口的開發人員造成問題。 

想象壹下,除此之外MyInterface,妳還有以下幾點:

雙方MyInterfaceSecondInterface包含壹個默認的方法完全相同的簽名(defaultMethod)。現在想象妳嘗試在同壹個類中使用這兩個接口: 

在這壹點上,妳有兩個相互沖突的實現defaultMethod,編譯器不知道應該使用哪種方法,所以妳會遇到編譯器錯誤。 

解決此問題的壹種方法是使用您自己的實現覆蓋沖突的方法:

另壹種解決方案是defaultMethod使用以下格式來指定要執行的版本:

所以如果妳想調用MyInterface#defaultMethod()實現,那麽妳會使用以下內容:

在Java 8界面中使用靜態方法

與默認方法類似,靜態接口方法為您提供了壹種在接口內定義方法的方法。但是,與默認方法不同,實現類不能覆蓋接口的靜態方法。 

如果妳有壹個特定於接口的靜態方法,那麽Java 8的靜態接口方法為妳提供了壹種將這些方法放入相應接口的方法,而不必將它們存儲在壹個單獨的類中。 

您可以通過將關鍵字static放在方法簽名的開頭創建靜態方法,例如:

當您實現包含靜態接口方法的接口時,該靜態方法仍然是接口的壹部分,並且不會由實現它的類繼承,因此您需要使用接口名稱為該方法添加前綴,例如: 

這也意味著類和接口可以具有相同簽名的靜態方法。例如,在同壹個類中使用MyClass.staticMethodMyInterface.staticMethod不會導致編譯時錯誤。 

那麽,接口本質上只是抽象類?

靜態接口方法和默認方法的增加導致壹些開發人員質疑Java接口是否變得更像抽象類。但是,即使添加了默認和靜態接口方法,接口和抽象類仍然存在壹些顯著差異: 

  • 抽象類可以有最終的,非最終的,靜態的和非靜態的變量,而壹個接口只能有靜態變量和最終變量。 
  • 抽象類允許妳聲明非靜態和最終的字段,而接口的字段本質上是靜態的和最終的。 
  • 在接口中,您聲明或定義為默認方法的所有方法都是天生公開的,而在抽象類中,您可以定義公共,受保護和私有的具體方法。 
  • 抽象類是類,因此可以具有狀態; 接口不能有與之相關的狀態。
  • 您可以在抽象類中定義構造函數,這在Java接口內是不可能的。
  • Java只允許妳擴展壹個類(不管它是否抽象),但是妳可以自由地實現盡可能多的接口。這意味著當妳需要多重繼承時,接口通常具有優勢,盡管妳需要小心死亡的致命鉆石! 

根據需要多次應用相同的註釋

傳統上,Java註釋的局限之壹在於,您不能在同壹位置多次應用相同的註釋。嘗試多次使用相同的註釋,並且您將遇到編譯時錯誤。 

但是,隨著Java 8重復註釋的引入,您現在可以在同壹位置隨意多次使用相同的註釋。

為了確保您的代碼與早期版本的Java保持兼容,您需要將重復註釋存儲在容器註釋中。

您可以通過完成以下步驟來告訴編譯器生成此容器:

  • @Repeatable meta-annotation(用於註釋註釋的註釋)標記問題中的註釋。 例如,如果妳想使@ToDo註解可重復,妳可以使用: 例如,如果妳想使@ToDo註解可重復,妳可以使用:
  • 括號中的值是編譯器最終生成的容器註釋的類型。 聲明包含註釋類型。這必須具有重復註釋類型數組的屬性,例如: 

嘗試多次應用相同的註釋而不首先聲明它是可重復的將在編譯時導致錯誤。但是,壹旦您指定這是可重復註釋,您可以在使用標準註釋的任何位置多次使用此註釋。 

結論

在我們的Java 8系列的第二部分中,我們看到了如何通過將lambda表達式與方法引用相結合,以及如何使用默認方法和靜態方法來增強接口,從而減少Android項目中更多的樣板代碼。 

在第三部分也是最後壹部分中,我們將看到壹個新的Java 8 API,它允許您以更高效的聲明方式處理大量數據,而不必擔心並發和線程管理。我們還將通過探索函數接口在lambda表達式,靜態接口方法,默認方法等中扮演的角色,將我們在本系列中討論的壹些不同功能捆綁在壹起。 

最後,即使我們仍然在等待Java 8的新日期和時間API正式到達Android,我將展示如何在今天的Android項目中開始使用這個新的API,並在第三方的幫助下庫。 

同時,查看壹些關於Java和Android應用程序開發的其他文章!

Envato qr branded
关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.