Java 中引入內(nèi)部類的意義?
前言
這是個好問題,因為它讓我想起來自己剛學(xué)到內(nèi)部類時候的“想用的沖動”。
導(dǎo)致我代碼里到處都是層層的內(nèi)部類套嵌。不但經(jīng)常搞得靜態(tài)域錯誤一堆(內(nèi)部類不允許有static成員字段),而且過一段時間自己都搞不清當(dāng)初寫的是什么。
一個很重要的設(shè)計準(zhǔn)則是:設(shè)計是做減法,能不用模式就不用模式。
這個準(zhǔn)則對內(nèi)部類來說同樣適用。
所以回答這個問題的基調(diào)應(yīng)該是:
能不用內(nèi)部類就不用內(nèi)部類。
實踐
我以前覺得內(nèi)部類用來有針對性地暴露外部類的特定接口,比一下子把整個對象都給人家要好。比如說下面代碼中的外部類Outer實現(xiàn)了三個接口方法,能跑,能飛,能思考。然后有三個方法getRunner(),getFlyer(),getThinker()有針對性地對外暴露部分功能接口。
public interface Runnable{ public void run(); } public interface Flyable{ public void fly(); } public interface Thinkable{ public void think(); } public class Outer{ public void run(){ //do something } public void fly(){ //do something } public void think(){ //do something } public class Runner implements Runnable{ public void run(){Outer.this.run();} } public class Flyer implements Flyable{ public void fly(){Outer.this.fly();} } public class Thinker implements Thinkable{ public void think(){Outer.this.think();} } public Runner getRunner(){return new Runner();} public Flyer getFlyer(){return new Flyer();} public Thinker getThinker(){return new Thinker();} }
但實際上直接實現(xiàn)三個接口不是就很好嘛。用內(nèi)部類根本算不上優(yōu)雅,多了很多代碼。僅僅為了暴露接口根本不需要使用內(nèi)部類。
public interface Runnable{ public void run(); } public interface Flyable{ public void fly(); } public interface Thinkable{ public void think(); } public class Outer implements,Runnable,Flyable,Thinkable{ public void run(){ //do something } public void fly(){ //do something } public void think(){ //do something } }
再或者說常見的控制框架。我們定義個Event接口,必須有action()方法。在外部類里定義事件處理的流程。然后定義了幾個實現(xiàn)Event接口的內(nèi)部類。
public interface Event{ public void action(); } public class Controller{ private int id; private List<Event> list=new ArrayList<Event>(); public void prepare(){ //put new events into the list } public void doEvents(){ //do every events in the list } public class A implements Event{ public void action(){ //do something } } public class B implements Event{ public void action(){ //do something } } }
但這也不是非內(nèi)部類不可。獨立定義A,B類,最后再把Event對象組合到Controller里完全可以,而且更簡潔易讀。
另外,說到內(nèi)部類,經(jīng)常會提到閉包,回調(diào)。但內(nèi)部類也不是唯一的方案。簡單的繼承,組合都能實現(xiàn)同等的數(shù)據(jù)封裝效果。
但Java到底需不需要內(nèi)部類?答案還是需要的。Java引入內(nèi)部類的真正意義就在于,還是有很多情況,沒有內(nèi)部類是處理不了的,或者用內(nèi)部類處理起來更加優(yōu)雅。
還是第一個例子。如果外部類不止有一種接口實現(xiàn)方法。如果我實現(xiàn)了Runnable接口,就只能定義一個run()方法。這時候內(nèi)部類就派用場了。比如,企鵝既會跑,又會游泳。所以它的兩個內(nèi)部類實現(xiàn)兩種不同的run()。可以返回兩種不同的Runnable引用。
public interface Runnable{ public void run(); } public class Penguin{ public void run(){ //do something } public void swim(){ //do something } public class Running implements Runnable{ public void run(){Penguin.this.run();} } public class Swimming implements Runnable{ public void run(){Penguin.this.swim();} } public Runner getRunner(){return new Running();} public Flyer getSwimmer(){return new Swimming();} }
另一種典型場景就是多繼承。如果外部類已經(jīng)繼承了某個基類,比如說企鵝繼承自鳥類。但Runnable這時候正好是一個abstract抽象類呢?Java不支持多繼承,內(nèi)部類可以解決這個問題。
public class Bird{ //some code here } public abstract class Runnable{ public abstract void run(); } public class Penguin extends Bird{ public class Runner extends Runnable{ //do something } public Runner penguinCanRun(){return new Runner();} }
剛才提到了控制框架可以不用內(nèi)部類。但實際上你看看事件驅(qū)動的Swing里到處都是內(nèi)部類。為什么呢?因為有大量的事件,而且多數(shù)事件的相應(yīng)方法只被用到了一次。用內(nèi)部類是為了控制類的數(shù)量,考慮的是更好地封裝。
內(nèi)部類另外一個好的特性就是它獨立于外部類,不會像組合一樣隨著外部類的初始化而一起被初始化。而是在我們需要它的時候再創(chuàng)建它。比如說容器里的迭代器,需要我們手動創(chuàng)建。作為可選組件存在于外部類中,不會增加外部類的負(fù)擔(dān)。
總之,要知道什么時候真正需要內(nèi)部類,先要搞清楚什么時候可以不用內(nèi)部類。內(nèi)部類不是大力丸,不要濫用內(nèi)部類
相關(guān)文章
Java中防止數(shù)據(jù)重復(fù)提交超簡單的6種方法
在平時開發(fā)中,如果網(wǎng)速比較慢的情況下,用戶提交表單后,發(fā)現(xiàn)服務(wù)器半天都沒有響應(yīng),那么用戶可能會以為是自己沒有提交表單,就會再點擊提交按鈕重復(fù)提交表單,這篇文章主要給大家介紹了關(guān)于Java中防止數(shù)據(jù)重復(fù)提交超簡單的6種方法,需要的朋友可以參考下2021-11-11tomcat啟動完成執(zhí)行 某個方法 定時任務(wù)(Spring)操作
這篇文章主要介紹了tomcat啟動完成執(zhí)行 某個方法 定時任務(wù)(Spring)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Java分布式鎖理論(redis、zookeeper))案例詳解
zookeeper有個節(jié)點路徑的概念,節(jié)點路徑不能重復(fù),保證了唯一性,這篇文章給大家介紹Java分布式鎖理論(redis、zookeeper)?案例詳解,感興趣的朋友跟隨小編一起看看吧2024-01-01spring?boot配置dubbo方式(properties)
這篇文章主要介紹了spring?boot配置dubbo方式(properties),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01SpringBoot中的配置類(@Configuration)
這篇文章主要介紹了SpringBoot中的配置類(@Configuration),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06