2010-02-17 28 views
20

在我的一些項目和某些書籍中,據說不是使用內部類(匿名或不是,靜態或不是) - 除了在某些受限制的條件下,如EventListener s或Runnable s - 是最佳做法。他們甚至在我的第一個行業項目中「禁止」。Java(匿名或不內部)內部類:使用它們很好嗎?

這真的是最佳做法嗎?爲什麼?

(我必須說,我使用了他們很多...)

- 編輯---
我不能選擇一個正確的答案,在所有這些迴應:有正當性的一部分主要是所有這些:我仍然會使用內部類,但我會盡量少用它們!

回答

25

在我的觀點認爲,Java代碼中90%的內部類是或者與單個類相關聯的實體,因此被「推入」爲內部類,或者因Java不支持Lambdas而存在的匿名內部類。

我個人不喜歡看複雜的內部類。它們增加了源文件的複雜性,使它變得更大,它們在調試和分析等方面很難處理。我喜歡將我的項目分成許多包,在這種情況下,我可以使大多數實體成爲頂級類只限於包裝。這些經常是匿名的,雖然我不是一個粉絲(在很多情況下會更喜歡Lambda),但我活着與他們在一起,但不喜歡他們。

我沒有做過任何C#在幾年,但我想知道如果內部類或任何時候,他們推出的Lambda表達式C#相當於被丟棄的流行。

+3

爲了便於參考,我必須提及** Java 8 **引入了[** Lambda表達式**](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。 – vellotis 2016-01-14 13:03:58

+1

此外,祝你好運單元測試內部類... – twiz 2016-03-29 01:40:19

16

清潔度。如果將代碼分解爲邏輯代碼,並不是所有的代碼都會融入相同的文件,那麼理解代碼會更容易。

這就是說,我不認爲明智地使用內部類是不合適的。有時候,這些內部類僅僅是爲了一個目的而存在的,所以我不會對它們在使用它們的唯一文件中存在任何問題。但是,這在我的經驗中並沒有發生。

+2

根據我的經驗,只要你做了一個內部類,你就需要它到別的地方:) – Dolph 2010-02-17 21:18:37

+12

如果它被分解成邏輯片斷,那麼它更容易理解代碼,是的 - 但是在*我的書中,這意味着最好定義一些儘可能接近它的地方,而不是將其任意分割成它自己的文件。 ;) – Porculus 2010-02-17 21:41:36

+2

內部類不排除你在他們居住的班級以外使用他們。枚舉是一個很好的例子,枚舉通常用於單個類,使其成爲一個公共靜態,然後你有一個上下文的地方,預計將使用枚舉。工廠模式是生成實現的內部類的好地方,而不會污染程序包名稱空間中的某些事物,這些事物只應在由Factory對象控制的特定情況下實例化。 – 2010-02-17 21:59:39

6

匿名類很適合在基於事件的編程時使用,特別是在swing中。

+1

爲什麼在基於事件的編程中,而不是其他用法,如用於重新組合衆多參數的結構,... 這更多的是教條而不是解釋:-1。 – Guillaume 2010-02-22 10:02:24

1

當試圖模擬多重繼承時,內部類是適當的。它類似於使用C++的情況:在C++中有多重繼承時,內存中的對象佈局實際上是多個對象實例的串聯;然後編譯器會調用方法調用時應如何調整「this」指針。在Java中,沒有多重繼承,但是可以使用內部類來爲給定實例提供另一種類型的「視圖」。

大多數時候,可以堅持單一繼承,但偶爾多重繼承是正確的工具,這是使用內部類的時候。

這意味着內部類在某種程度上比普通類更復雜,就像多重繼承比單一繼承更復雜一樣:許多程序員在圍繞這個概念時都會遇到一些麻煩。因此,「最佳實踐」:避免內部類,因爲它會讓你的同事感到困惑。在我看來,這不是一個好的論點,在我的工作場所,我們很高興在我們認爲合適的時候使用內部類。

(內部類的小缺點在於,它們加在源代碼中縮進的一個額外的水平,這是在時間有點令人厭煩的,當一個希望保持79列中的代碼。)

1

當我們需要使用一個方法實現接口時,通常會使用匿名內部類,如Runnable,ActionListener和其他方法。匿名內部類的

一個更偉大的應用是,當你不想做一些類的子類,但你需要重寫它的方法一個(或兩個)。

當您想要在兩個類之間實現緊密的連貫性時,可以使用命名的內部類。它們並不像匿名內部類那樣有用,我不能肯定這是一個很好的習慣。

Java也有嵌套(或內部靜態)類。當你想提供一些特殊的訪問權限時,可以使用它們,標準的公共或默認訪問級別是不夠的。

0

是的,它是很好的使用它們,當你試圖保持一個類的凝聚力,並且類不應該從外部類的外部實例化,使構造函數是私人的,你有非常好的凝聚力封裝。任何人都說你應該從不使用它們不知道他們在說什麼。對於事件處理程序和匿名內部類擅長的其他事情來說,它們比使用許多僅適用於特定類的事件處理程序混淆包名稱空間的替代方法要好得多。

1

內部類通常用於「傳遞行爲」作爲方法的參數。具有閉包的其他語言可以很好地支持此功能。 由於語言限制,使用內部類會產生一些不優雅的代碼(恕我直言),但它有用並且廣泛用於處理事件,並且通常與內部類一起使用。

所以我會說,內部類是非常有用的。

3

匿名內部類能夠查看「新」語句周圍的字段和變量。這可以設計出一些非常乾淨的設計,並且對於「我們如何製作一個簡單版本的lambda表達式」是一個相當不錯的(但有點羅嗦)的方法。

命名內部類具有一個名字,希望告知的權益,其可以以通常的方式進行記錄,但其被連接在一起到周圍的類。一個非常好的例子是Builder模式,其中內部類負責爲初始化過程提供狀態,而不是有許多構造函數。這樣的構建者不能在類之間重用,所以讓構建者與父類緊密聯繫是非常合理的。

4

是,禁止內部類是一種有益的做法,在找出一個地方禁止他們是警告我關在那裏工作,所以保留我的未來理智的好方法。 :)

由於gicappa指出,匿名內部類是最接近的Java必須關閉,且非常適合在情況下使用合格的行爲放到一個方法是合適的,如果沒有別的。

+1

至少這一個讓我微笑,這是一個很好的用法,在公司的編碼政策中發現一些愚蠢的規則! – Guillaume 2010-02-22 10:04:23

+0

關於事件處理程序沒有具體的內容,嵌套類的主要目的是封裝。 – bharatj 2015-09-18 18:14:47

2

某些框架,如Wicket,確實需要匿名內部類。

說永遠是愚蠢的。永不說永不!一個好用的例子可能是你的某些遺留代碼是由許多類直接在集合字段上操作的人編寫的,無論出於何種原因,你都不能更改這些其他類,但需要有條件地將操作鏡像到另一個類採集。最簡單的做法是通過匿名內部類添加此行爲。

bagOfStuff = new HashSet(){ 
    @Override 
    public boolean add(Object o) { 
    boolean returnValue = super.add(o); 
    if(returnValue && o instanceof Job) 
    { 
     Job job = ((Job)o); 
     if(job.fooBar()) 
     otherBagOfStuff.add(job); 
    } 
    return returnValue; 
    } 
} 

也就是說,他們絕對可以像窮人的封閉一樣使用。

2

正如一些人所說,很多時候,當你使用一個匿名內部類,它也被用在其他一些地方也...

因此,你可以很容易複製內部類代碼很多地方... 當你使用非常簡單的內部類來過濾/排序集合,使用謂詞,比較器或類似的東西時,這似乎不是問題...

但是你必須知道,當你使用3次匿名內部類完全一樣的東西(例如刪除一個Collection的「」),你實際上是在java PermGen上創建了3個新類。

因此,如果每個人在任何地方都使用內部類,這可能會導致應用程序擁有更大的permgen。根據應用程序,這可能是一個問題...如果你正在從事的行業,你可能編程嵌入式應用程序具有有限的內存,應該優化...

注意這也是爲什麼雙捲曲括號語法(匿名內部類與非靜態初始化塊)有時被認爲是反模式:

new ArrayList<String>() {{ 
    add("java"); 
    add("jsp"); 
    add("servlets"); 
    }} 

你應該問的人誰禁止你使用它們... 恕我直言,這一切都取決於上下文..

+0

這是反對內心階層的好處。至少在宣佈一些「標準」行爲時。 – Guillaume 2011-08-16 16:20:15

+0

這是內心階層的愚蠢使用。爲什麼不創建數組列表並添加字符串?顯示內部類的一個愚蠢的例子並不能完全利用它們的壞習慣。 – ncmathsadist 2013-12-21 03:16:51

+0

@ncmathsadist我不認爲它很好,但有些人發現它更具可讀性,並在單元測試中使用它,而permgen並不是什麼大問題 – 2013-12-21 11:59:06

3

我建議在使用它時要小心,如果它需要方法參數的話。我剛剛發現與此相關的內存泄漏。它涉及使用GrizzlyContinuation的HttpServlet。
總之這裏是bug的代碼:

public void doGet(HttpServletRequest request, final HttpServletResponse response){ 
    createSubscription(..., new SubscriptionListener(){ 
    public void subscriptionCreated(final CallController controller) { 
     response.setStatus(200); 
     ... 
     controller.resume(); 
    } 

    public void subscriptionFailed(){ 
     ... 
    } 

    public void subscriptionTimeout(){ 
     ... 
    }}); 
} 

如此以來,聽衆是通過訂閱的HttpServletResponse的也保持的情況下,監聽器需要它(不是很明顯)保持。然後,只有訂閱被刪除時,HttpServletResponse實例纔會被釋放。如果你使用一個內部類來獲得它的構造函數中的響應,那麼一旦調用恢復釋放內存,它就可以被設置爲null。

使用它們但要小心!

就是這裏所說

馬丁

2

一個項目是一個(非靜態)內部類進行的引用,它的封閉類。更重要的是,內部類可以訪問封閉類的私有成員。它可能會破壞封裝。

如果您有選項,請勿使用內部類。

+0

靜態內部類?不要如果它是從屬於另一個類,使它在同一個文件中的非公共類。沒有爲監聽者使用內部類意味着維護getter的痛苦鏈,並相互鏈​​接狀態變量,這些變量只是一個醜陋的散列。 – ncmathsadist 2013-12-21 03:18:35