Java的內部類與向上轉型
相關文章:
內部類及其建立方式
在前一篇的文章中,我們討論了內部類的幾種建立的方法,但是,我們會注意到,這些內部類的無論是類的定義還是用來得到其物件的get方法都是public
的,因此在其它類中可以直接得到這樣的內部類的物件。而在實際的應用中,不同於外部類的只能設計成public
與default
,很多時候內部類的定義是為private
或protected
的,此時,我們怎樣去使用這些內部類呢?
若我們直接像之前的方法一樣去使用這樣的內部類,如以下程式碼:
public class PizzaStore {
private class SausagePizza{
int size;
private SausagePizza(int size) {this.size = size;}
public void getName() {System.out.println("Got a SausagePizza of size:\t" + size);}
public int getSize() {return size;}
}
public SausagePizza getSausagePizza(int size) {
return new SausagePizza(size);
}
}
在這裡,我們見到了熟悉的披薩商店,而其中的SausagePizza
在這裡被定義為private型別,因此,若我們在另一個測試類Test
中來直接建立這個類的物件,像如下這樣:
public class Test {
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
PizzaStore.SausagePizza pizza = store.getSausagePizza(5);
}
}
便會出現錯誤:
Error:(6, 19) java: learning_java.InnerClassTest.PizzaStore.SausagePizza 在 learning_java.InnerClassTest.PizzaStore 中是 private 訪問控制
因此,我們可以看到,對於私有的內部類,我們是不能直接在其餘的類中建立其物件的,因為我們在類Test
中甚至無法獲取到PizzaStore.SausagePizza
,便無法實現宣告,更不用說去建立物件了。
那麼,是不是我們無法在除了PizzaStore
類之外的類中得到一個披薩的例項呢?
答案是既然允許這樣定義內部類,我們自然可以獲取這樣的例項。但是,在這裡我們需要藉助向上轉型為介面的方式來實現。為什麼要藉助向上轉型為介面呢?因為仔細觀察上面的報錯,其實我們之所以無法得到一個SausagePizza
的例項,在阻礙我們的僅僅是在我們想要獲取其例項的時候,無法對其進行宣告PizzaStore.SausagePizza
而已,而如果這個類是某個介面的實現的話呢?那麼我們直接用介面宣告不就可以了麼?
下面我們繼續用我們的披薩商店來作為例子,來看一下我們怎樣得到這樣的內部類的例項。
首先,我們先定義一個披薩的介面Pizza
:
public interface Pizza {
public void getName();
}
然後,我們定義一個起司披薩的內部類來實現這個介面
public class PizzaStore {
private class CheesePizza implements Pizza {
private int size;
private CheesePizza(int size) {this.size = size;}
public void getName() {System.out.println("Got a CheesePizza of size:\t" + size);}
public int getSize() {return size;}
}
private class SausagePizza{
private int size;
private SausagePizza(int size) {this.size = size;}
public void getName() {System.out.println("Got a SausagePizza of size:\t" + size);}
public int getSize() {return size;}
}
public Pizza getCheesePizza(int size) {
return new CheesePizza(size);
}
public SausagePizza getSausagePizza(int size) {
return new SausagePizza(size);
}
}
我們可以看到,在這裡新定義的起司披薩CheesePizza
與之前的香腸披薩SausagePizza
在定義上沒有任何不同,只是實現了Pizza
介面而已,而用於建立這個內部類的例項的方法getCheesePizza
的型別也用其實現的介面Pizza
來進行宣告,這樣,我們在測試程式中便可以用介面的型別Pizza
來宣告我們所想要建立的例項,如下:
public class Test {
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
Pizza pizza = store.getCheesePizza(5);
pizza.getName();
}
}
程式執行結果:
Got a CheesePizza of size: 5
大功告成!
但是,有一點我們需要注意,我們在介面中僅定義了一個方法getName()
,而在Test
中宣告的例項變數pizza
的型別為介面Pizza
,因此對於這個例項,我們只能呼叫getName()
方法,而不能呼叫我們在內部類CheesePizza
時定義的另一個方法getSize()
,否則會報錯,例子如下:
public class Test {
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
Pizza pizza = store.getCheesePizza(5);
pizza.getName();
// getSize()方法並沒有在介面Pizza中宣告,因此在這裡並不能呼叫該方法
pizza.getSize();
}
}
編譯報錯:
Error:(8, 14) java: 找不到符號
符號: 方法 getSize()
位置: 型別為learning_java.InnerClassTest.Pizza的變數 pizza
由這一點,我們可以看到,private
型別的內部類,在其它類中是完全不可見的,且其所實現介面之外的所有方法是不可用的。在這裡,我們所得到的只是指向基類或介面的引用,所以能夠很方便地隱藏實現細節。
由於不能訪問任何新增加的、原本不屬於公共介面的方法,所以擴充套件介面是沒有太多必要的。