Java 中引入內(nèi)部類(lèi)的意義?
前言
這是個(gè)好問(wèn)題,因?yàn)樗屛蚁肫饋?lái)自己剛學(xué)到內(nèi)部類(lèi)時(shí)候的“想用的沖動(dòng)”。
導(dǎo)致我代碼里到處都是層層的內(nèi)部類(lèi)套嵌。不但經(jīng)常搞得靜態(tài)域錯(cuò)誤一堆(內(nèi)部類(lèi)不允許有static成員字段),而且過(guò)一段時(shí)間自己都搞不清當(dāng)初寫(xiě)的是什么。
一個(gè)很重要的設(shè)計(jì)準(zhǔn)則是:設(shè)計(jì)是做減法,能不用模式就不用模式。
這個(gè)準(zhǔn)則對(duì)內(nèi)部類(lèi)來(lái)說(shuō)同樣適用。
所以回答這個(gè)問(wèn)題的基調(diào)應(yīng)該是:
能不用內(nèi)部類(lèi)就不用內(nèi)部類(lèi)。
實(shí)踐
我以前覺(jué)得內(nèi)部類(lèi)用來(lái)有針對(duì)性地暴露外部類(lèi)的特定接口,比一下子把整個(gè)對(duì)象都給人家要好。比如說(shuō)下面代碼中的外部類(lèi)Outer實(shí)現(xiàn)了三個(gè)接口方法,能跑,能飛,能思考。然后有三個(gè)方法getRunner(),getFlyer(),getThinker()有針對(duì)性地對(duì)外暴露部分功能接口。
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();} }
但實(shí)際上直接實(shí)現(xiàn)三個(gè)接口不是就很好嘛。用內(nèi)部類(lèi)根本算不上優(yōu)雅,多了很多代碼。僅僅為了暴露接口根本不需要使用內(nèi)部類(lè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 } }
再或者說(shuō)常見(jiàn)的控制框架。我們定義個(gè)Event接口,必須有action()方法。在外部類(lèi)里定義事件處理的流程。然后定義了幾個(gè)實(shí)現(xiàn)Event接口的內(nèi)部類(lè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)部類(lèi)不可。獨(dú)立定義A,B類(lèi),最后再把Event對(duì)象組合到Controller里完全可以,而且更簡(jiǎn)潔易讀。
另外,說(shuō)到內(nèi)部類(lèi),經(jīng)常會(huì)提到閉包,回調(diào)。但內(nèi)部類(lèi)也不是唯一的方案。簡(jiǎn)單的繼承,組合都能實(shí)現(xiàn)同等的數(shù)據(jù)封裝效果。
但Java到底需不需要內(nèi)部類(lèi)?答案還是需要的。Java引入內(nèi)部類(lèi)的真正意義就在于,還是有很多情況,沒(méi)有內(nèi)部類(lèi)是處理不了的,或者用內(nèi)部類(lèi)處理起來(lái)更加優(yōu)雅。
還是第一個(gè)例子。如果外部類(lèi)不止有一種接口實(shí)現(xiàn)方法。如果我實(shí)現(xiàn)了Runnable接口,就只能定義一個(gè)run()方法。這時(shí)候內(nèi)部類(lèi)就派用場(chǎng)了。比如,企鵝既會(huì)跑,又會(huì)游泳。所以它的兩個(gè)內(nèi)部類(lèi)實(shí)現(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();} }
另一種典型場(chǎng)景就是多繼承。如果外部類(lèi)已經(jīng)繼承了某個(gè)基類(lèi),比如說(shuō)企鵝繼承自鳥(niǎo)類(lèi)。但Runnable這時(shí)候正好是一個(gè)abstract抽象類(lèi)呢?Java不支持多繼承,內(nèi)部類(lèi)可以解決這個(gè)問(wèn)題。
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)部類(lèi)。但實(shí)際上你看看事件驅(qū)動(dòng)的Swing里到處都是內(nèi)部類(lèi)。為什么呢?因?yàn)橛写罅康氖录?,而且多?shù)事件的相應(yīng)方法只被用到了一次。用內(nèi)部類(lèi)是為了控制類(lèi)的數(shù)量,考慮的是更好地封裝。
內(nèi)部類(lèi)另外一個(gè)好的特性就是它獨(dú)立于外部類(lèi),不會(huì)像組合一樣隨著外部類(lèi)的初始化而一起被初始化。而是在我們需要它的時(shí)候再創(chuàng)建它。比如說(shuō)容器里的迭代器,需要我們手動(dòng)創(chuàng)建。作為可選組件存在于外部類(lèi)中,不會(huì)增加外部類(lèi)的負(fù)擔(dān)。
總之,要知道什么時(shí)候真正需要內(nèi)部類(lèi),先要搞清楚什么時(shí)候可以不用內(nèi)部類(lèi)。內(nèi)部類(lèi)不是大力丸,不要濫用內(nèi)部類(lèi)
- java 利用反射獲取內(nèi)部類(lèi)靜態(tài)成員變量的值操作
- java中的內(nèi)部類(lèi)內(nèi)部接口用法說(shuō)明
- Java匿名內(nèi)部類(lèi)的寫(xiě)法示例
- Java匿名類(lèi),匿名內(nèi)部類(lèi)實(shí)例分析
- Java內(nèi)部類(lèi)應(yīng)用之靜態(tài)內(nèi)部類(lèi)應(yīng)用示例
- Java局部?jī)?nèi)部類(lèi)和匿名內(nèi)部類(lèi)定義與用法實(shí)例分析
- Java干貨知識(shí)深入理解內(nèi)部類(lèi)
- java內(nèi)部類(lèi)原理與用法詳解
- 淺談Java面向?qū)ο笾畠?nèi)部類(lèi)
相關(guān)文章
如何在IDEA中查看依賴(lài)關(guān)系的方法步驟
這篇文章主要介紹了如何在IDEA中查看依賴(lài)關(guān)系的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Spring事件監(jiān)聽(tīng)基本原理與使用詳解
這篇文章主要介紹了Spring事件監(jiān)聽(tīng)基本原理與使用詳解,Spring的事件監(jiān)聽(tīng)機(jī)制和發(fā)布訂閱機(jī)制是很相似的:發(fā)布了一個(gè)事件后,監(jiān)聽(tīng)該類(lèi)型事件的所有監(jiān)聽(tīng)器會(huì)觸發(fā)相應(yīng)的處理邏輯,需要的朋友可以參考下2024-01-01
淺談spring中isolation和propagation的用法
這篇文章主要介紹了淺談spring中isolation 和propagation的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
SpringBoot中日志切面實(shí)現(xiàn)小結(jié)
本文介紹了SpringBoot中日志切面實(shí)現(xiàn)小結(jié),通過(guò)定義一個(gè)自定義注解和創(chuàng)建一個(gè)日志切面類(lèi),為方法添加日志記錄功能,感興趣的可以了解一下2024-11-11
在Spring boot的項(xiàng)目中使用Junit進(jìn)行單體測(cè)試
今天小編就為大家分享一篇關(guān)于spring boot使用Junit進(jìn)行測(cè)試,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
java 線(xiàn)程之對(duì)象的同步和異步(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇java 線(xiàn)程之對(duì)象的同步和異步(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
IDEA?報(bào)Plugin'maven-resources-plugin:'not?found?
如果在使用?IDEA?時(shí)遇到?"Plugin?'maven-resources-plugin:'?not?found"?錯(cuò)誤,可能是由于?Maven?倉(cāng)庫(kù)中未找到所需的?Maven?插件,近小編給大家分享幾種解決方法,感興趣的朋友跟隨小編一起看看吧2023-07-07
SpringMVC框架實(shí)現(xiàn)圖片上傳與下載
這篇文章主要為大家詳細(xì)介紹了SpringMVC框架實(shí)現(xiàn)圖片上傳與下載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
SpringBoot中的靜態(tài)資源訪(fǎng)問(wèn)的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot中的靜態(tài)資源訪(fǎng)問(wèn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

