巢狀類
| 瀏覽 類和物件 主題: |
在 Java 中,你可以在一個類中定義另一個類。一個類可以巢狀在另一個類中,或者巢狀在一個方法中。一個沒有巢狀的類被稱為頂級類,而定義巢狀類的類則被稱為外部類。
當一個類在另一個類中宣告時,巢狀類的訪問修飾符可以是 public、private、protected 或 package(預設)。
程式碼清單 4.10:OuterClass.java
public class OuterClass {
private String outerInstanceVar;
public class InnerClass {
public void printVars() {
System.out.println("Print Outer Class Instance Var.:" + outerInstanceVar);
}
}
}
|
內部類可以訪問封閉類例項的變數和方法,即使是私有的變數和方法,如上所示。這使得它與 C++ 中的巢狀類非常不同,C++ 中的巢狀類等效於“靜態”內部類,見下文。
內部物件有一個對外部物件的引用。換句話說,所有內部物件都與外部物件繫結。內部物件只能透過對“外部”物件的引用來建立。見下文。
程式碼段 4.20:外部類呼叫。
public void testInner() {
...
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
...
}
|
請注意,內部物件,因為它們與外部物件繫結,不能包含靜態變數或方法。
當在外部類的非靜態方法中時,你可以直接使用 new InnerClass(),因為類例項隱含地為 this。
你可以使用語法 OuterClass.this 從內部類中直接訪問對外部物件的引用;儘管這通常是不必要的,因為你已經可以訪問它的欄位和方法。
內部類編譯成單獨的“.class”位元組碼檔案,檔名是封閉類的名稱,後跟一個“$”,然後是內部類的名稱。例如,上面的內部類將編譯成一個名為“OuterClass$InnerClass.class”的檔案。
巢狀類可以宣告為靜態。這些類不繫結到外部定義類的例項。靜態巢狀類沒有封閉例項,因此不能訪問外部類的例項變數和方法。在建立靜態內部類時,你無需指定例項。這等效於 C++ 中的內部類。
這些內部類,也稱為區域性類,不能有訪問修飾符,就像區域性變數一樣,因為該類對方法是“私有”的。內部類只能是 抽象 或 最終。
程式碼清單 4.11:OuterClass.java
public class OuterClass {
public void method() {
class InnerClass {
}
}
}
|
除了封閉類的例項變數外,區域性類還可以訪問封閉方法的區域性變數,但僅限於宣告為final的變數。這是因為區域性類例項可能比方法的呼叫持續時間更長,因此需要它自己的變數副本。為了避免在同一作用域中具有兩個名稱相同但可變的變數副本的問題,要求該變數必須是final的,因此不能更改。
在Java中,類的定義及其例項化可以組合成一個步驟。透過這樣做,類不需要名稱。這些類稱為匿名類。匿名類可以在可以使用引用的上下文中定義和例項化,並且它是現有類的巢狀類。匿名類是區域性於方法的類的特例;因此,它們也可以訪問封閉方法的final區域性變數。
匿名類最常用於建立介面或介面卡類的例項,而無需建立全新的類。
程式碼清單 4.12:ActionListener.java
public interface ActionListener {
public void actionPerformed();
}
|
程式碼段 4.21:匿名類。
ActionListener listener = new ActionListener() {
public void actionPerformed() {
// Implementation of the action event
...
return;
}
};
|
在上面的示例中,實現ActionListener的類是匿名的。該類在其被例項化的位置定義。
以上程式碼比顯式定義類更難閱讀,那麼為什麼要使用它呢?如果需要介面的許多實現,這些類僅在一個特定位置使用,並且很難為它們想出名稱,使用匿名內部類是有意義的。
以下示例使用匿名內部類來實現動作監聽器。
程式碼清單 4.13:MyApp.java
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyApp {
Button aButton = new Button();
MyApp() {
aButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello There");
}
}
);
}
}
|
以下示例執行相同的操作,但它為實現動作監聽器的類命名。
程式碼清單 4.14:MyApp.java
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyApp {
Button aButton = new Button();
// Nested class to implement the action listener
class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello There");
}
}
MyApp() {
aButton.addActionListener(new MyActionListener());
}
}
|
當您打算使用許多不同的類,並且每個類都實現相同的介面時,使用匿名類尤其合適。