1. 程式人生 > >Java的內部類與向上轉型

Java的內部類與向上轉型

相關文章:
內部類及其建立方式

在前一篇的文章中,我們討論了內部類的幾種建立的方法,但是,我們會注意到,這些內部類的無論是類的定義還是用來得到其物件的get方法都是public的,因此在其它類中可以直接得到這樣的內部類的物件。而在實際的應用中,不同於外部類的只能設計成publicdefault,很多時候內部類的定義是為privateprotected的,此時,我們怎樣去使用這些內部類呢?

若我們直接像之前的方法一樣去使用這樣的內部類,如以下程式碼:

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型別的內部類,在其它類中是完全不可見的,且其所實現介面之外的所有方法是不可用的。在這裡,我們所得到的只是指向基類或介面的引用,所以能夠很方便地隱藏實現細節。

由於不能訪問任何新增加的、原本不屬於公共介面的方法,所以擴充套件介面是沒有太多必要的。