聊聊關(guān)于Java方法重寫的反思
最近在開發(fā)中遇到一個(gè)關(guān)于Java方法重寫的一些問題,對(duì)于方法重寫的用法以及可能導(dǎo)致的問題產(chǎn)生了一些思考,本文用于記錄下這些想法。
問題場(chǎng)景
我們首先來看兩段代碼:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case TAKE_PHOTO_CODE:{
//處理拍照得到的結(jié)果
break;
}
case CHOOSE_FROM_ALBUM_CODE:{
//處理相冊(cè)選取到的結(jié)果
break;
}
}
}@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch (requestCode){
case TAKE_PHOTO_CODE:{
//處理拍照得到的結(jié)果
break;
}
case CHOOSE_FROM_ALBUM_CODE:{
//處理相冊(cè)選取到的結(jié)果
break;
}
default:{
super.onActivityResult(requestCode, resultCode, data);
}
}
}這兩段代碼是Android開發(fā)中處理Activity結(jié)果的示例。Android啟動(dòng)新頁(yè)面后,新頁(yè)面設(shè)置完結(jié)果返回的時(shí)候,舊頁(yè)面可以從這個(gè)方法得到新頁(yè)面的結(jié)果。來自不同頁(yè)面的結(jié)果按照參數(shù)中的requestCode來區(qū)分,這個(gè)requestCode和啟動(dòng)新頁(yè)面時(shí)傳遞的對(duì)應(yīng),也就是說一個(gè)requestCode標(biāo)識(shí)一個(gè)頁(yè)面請(qǐng)求和一個(gè)結(jié)果類型。例如,上面示例模擬的是常見APP中換用戶頭像的功能,結(jié)果有兩種:1. 拍照得到的結(jié)果;2. 相冊(cè)選取得到的結(jié)果。
上面兩種方法就結(jié)果來說都是對(duì)的,但是表達(dá)的意義不同:第一種寫法是純粹地?cái)U(kuò)展父類的方法,父類干的事它都干;而第二種寫法是改寫父類的方法,相當(dāng)于重定義并依賴了父類的行為,或者說對(duì)父類行為做了攔截、訪問控制。
原本Activity類中默認(rèn)實(shí)現(xiàn)是個(gè)空方法:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}這種情況下兩種寫法的行為差異完全可以忽略不計(jì),但是實(shí)際開發(fā)中我們一般繼承自FragmentActivity或AppCompatActivity,這兩個(gè)類都對(duì)這個(gè)方法做了相應(yīng)的實(shí)現(xiàn),在這種情況下,第一種寫法父類的實(shí)現(xiàn)一定會(huì)被執(zhí)行,但是第二種寫法可能將父類的實(shí)現(xiàn)短路了。這可能導(dǎo)致一些意料之外的問題,比如,Activity和Fragment都對(duì)某個(gè)requestCode進(jìn)行處理,但第二種寫法會(huì)導(dǎo)致Fragment的對(duì)應(yīng)onActivityResult方法不會(huì)被掉用。
在實(shí)際開發(fā)中我們可能會(huì)編寫一個(gè)BaseActivity,將一些方法實(shí)現(xiàn)一下并添加統(tǒng)計(jì)和日志,那么第二種寫法也可能導(dǎo)致日志丟失的問題。
問題分析
這個(gè)問題讓我聯(lián)想到一個(gè)設(shè)計(jì)原則:里氏替換原則(Liskov Substitution principle)。這個(gè)原則說明:派生類(子類)對(duì)象可以在程序中代替其基類(超類)對(duì)象。這表示程序中任何父類對(duì)象可以出現(xiàn)的位置,子類的對(duì)象都可將其替代。進(jìn)一步解讀,就是意味著子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能。
這個(gè)原則考慮了安全性。編程時(shí)為了降低耦合度,通常面向抽象數(shù)據(jù)類型(例如接口、抽象類等)來編寫,而父類在編寫的時(shí)候也不會(huì)去考慮子類的實(shí)現(xiàn),那么就要求子類的實(shí)現(xiàn)的時(shí)候需要顧及父類的運(yùn)行。
那么當(dāng)我們?cè)谥貙懜割惙椒ǖ臅r(shí)候,情況就復(fù)雜了起來,具體分為以下幾種情況:
- 當(dāng)父類代碼和子類代碼都是同一個(gè)人負(fù)責(zé)的時(shí)候,并且在代碼同一項(xiàng)目、同一模塊。這種情況比較安全,因?yàn)榫帉懽宇悓?shí)現(xiàn)的人是完全了解并掌控父類實(shí)現(xiàn)的;
- 當(dāng)父類代碼和子類代碼是同一個(gè)人負(fù)責(zé)的時(shí)候,而代碼位于不同項(xiàng)目。例如,一個(gè)人同時(shí)維護(hù)一個(gè)應(yīng)用項(xiàng)目和一個(gè)獨(dú)立框架。這種情況,就可能出隱患,因?yàn)殡S著項(xiàng)目進(jìn)行,這個(gè)框架中的父類可能被多個(gè)應(yīng)用項(xiàng)目使用,這個(gè)父類就可能無(wú)法兼顧多個(gè)項(xiàng)目的場(chǎng)景和用法,而導(dǎo)致子類實(shí)現(xiàn)中錯(cuò)誤地改寫父類的方法。
- 當(dāng)父類代碼和子類代碼時(shí)不同的人負(fù)責(zé),且代碼位于不同項(xiàng)目時(shí),這種情況就比較危險(xiǎn)了。因?yàn)楦割悓?shí)現(xiàn)的行為實(shí)現(xiàn)和行為變更很可能是不透明的、未知的,而且父類的實(shí)現(xiàn)可能不會(huì)顧及到子類的應(yīng)用。那么當(dāng)子類改寫父類行為的時(shí)候,當(dāng)父類行為發(fā)生變更,那么子類的實(shí)現(xiàn)很可能是有問題的。
方法與建議
針對(duì)上面所提到的三種情況,我思考了如下三個(gè)對(duì)應(yīng)的建議:
- 針對(duì)第一種安全的情況,盡量不改寫父類方法,在子類和父類實(shí)現(xiàn)中盡量補(bǔ)充注釋和注解說明;
- 針對(duì)第二種有隱患的情況,盡量不改寫父類方法,父類設(shè)計(jì)無(wú)法涵蓋所有場(chǎng)景時(shí),適當(dāng)時(shí)候重構(gòu)父類代碼,而不是讓子類通過“hack”的手段曲線救國(guó)。
- 針對(duì)第三種危險(xiǎn)的情況,一定不要改寫父類方法,可以考慮在方法第一行就
super調(diào)用。
到此這篇關(guān)于聊聊關(guān)于Java方法重寫的反思的文章就介紹到這了,更多相關(guān)Java方法重寫內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Security攔截器引起Java CORS跨域失敗的問題及解決
這篇文章主要介紹了Spring Security攔截器引起Java CORS跨域失敗的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java基礎(chǔ)學(xué)習(xí)之ArrayList類概述與常用方法
這篇文章主要為大家簡(jiǎn)單的介紹Java中ArrayList類的概述、常用方法及存儲(chǔ)字符串并遍歷,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-08-08
深入分析Spring BeanDefinition的構(gòu)造元信息
Bean Definition是一個(gè)包含Bean元數(shù)據(jù)的對(duì)象,它描述了如何創(chuàng)建Bean實(shí)例、Bean屬性的值以及Bean之間的依賴關(guān)系,本文將帶大家深入分析Spring BeanDefinition的構(gòu)造元信息,需要的朋友可以參考下2024-01-01
HttpServletResponse亂碼問題_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了HttpServletResponse亂碼問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
Java編程實(shí)現(xiàn)非對(duì)稱加密的方法詳解
這篇文章主要介紹了Java編程實(shí)現(xiàn)非對(duì)稱加密的方法,簡(jiǎn)單講述了非對(duì)稱加密的概念、原理,并結(jié)合實(shí)例形式分析了java實(shí)現(xiàn)DH加密解密、RSA加密解密、ElGamal加密等具體操作技巧,需要的朋友可以參考下2017-08-08
詳解Spring Data JPA動(dòng)態(tài)條件查詢的寫法
本篇文章主要介紹了Spring Data JPA動(dòng)態(tài)條件查詢的寫法 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Flutter ListView 上拉加載更多下拉刷新功能實(shí)現(xiàn)方法
這篇文章主要介紹了Flutter ListView 上拉加載更多下拉刷新功能實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07
Java Graphics實(shí)現(xiàn)界面顯示文字并換行
Java中Graphics類提供了一些基本的幾何圖形繪制方法,本文將利用Graphics實(shí)現(xiàn)界面顯示文字并換行效果,感興趣的小伙伴可以動(dòng)手嘗試一下2022-08-08
解決IDEA導(dǎo)入javaWeb項(xiàng)目注解爆紅的問題
這篇文章主要介紹了解決IDEA導(dǎo)入javaWeb項(xiàng)目注解爆紅的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

