Java入門系列之重寫

前言

關於所有Java系列文章面向有一定基礎的童鞋,所寫每一篇希望有一定含金量,有些內容可能會從Java整個語法全局考慮穿插後續要講解的內容以成系統,若不理解,請看完後再學習。上一節我們講解完了final關鍵字,本節我們繼續來對比講解Java和C#中的重寫,二者語言的重寫區分非常清晰,Java子類中基類方法簽名一樣或通過註解@Override顯式聲明,C#中基類通過virtual關鍵字修飾,子類通過ovveride關鍵字表示重寫,具體細節請往下看。

重寫

既然是重寫必然就涉及到繼承,我們首先來看看Java中的重寫是怎樣的呢?如下:

public class Main {
    public void f() {
        System.out.println("Main.f");
    }

    public static void main(String[] args) {
        Main main = new Sub();
        main.f();
    }
}

class Sub extends Main {
    public void f() {
        System.out.println("Sub.f");
    }
}

當調用基類的f方法時,此時發現已被子類所重寫,所以如上正常打印出Sub.f,要是我們將上述基類中方法修改爲私有的呢

可能我們期待輸出子類中的打印結果,但是修改爲私有後,說明此時對子類不再可見,也就相當於使用了final,在這種情況下,子類中方法則是一個全新的方法,很顯然說明:只有非私有方法纔可以被重寫。對於這種情況下編譯不會報錯, 但是也不會按照我們所期望的結果來執行,所以建議對於基類中的私有方法命名爲和子類不同的名字,爲了讓重寫一目瞭然或更加明確,在1.5版本發佈了註解功能,我們可以通過註解來顯式聲明要重寫基類方法,若基類爲私有,此時通過註解則會編譯報錯,因爲找不到要重寫的方法,這種體驗更加友好,比如如下:

public class Main {
    private void f() {
        System.out.println("Main.f");
    }

    public static void main(String[] args) {
        Main main = new Sub();
        main.f();
    }
}

class Sub extends Main {

    //編譯錯誤,未找到基類(超類)中要重寫的方法
    @Override
    public void f() {
        System.out.println("Sub.f");
    }
}

舉一反三,我們來思考一個問題,是不是方法簽名一致,子類就可以重寫基類方法呢?很顯然不是,若是靜態方法,必然不能被重寫,如果通過註解@Override聲明那麼必然編譯報錯,否則調用基類方法,對於接口中的靜態方法同理。所以還是建議使用註解來聲明重寫。那麼爲什麼通過註解顯式聲明重寫基類方法或通過關鍵字final修飾方法就會在編譯階段報錯呢?因爲它們在編譯階段就完成了靜態綁定,而不是運行時動態綁定。問題又來了,上述我們講解到若在子類中不通過註解顯式聲明重寫,同時在基類中方法私有,此時一定可以編譯通過(上述已演示),並且會調用基類方法並打印出結果,事實情況一定是這樣嗎?很顯然也不是如此,如下示例:

class Super {
    private void f() {
        System.out.println("Super.f");
    }
}

class Derived extends Super {
    public void f() {
        System.out.println("Derived.f");
    }
    public static void main(String[] args) {
        Super aSuper = new Derived();
        
        //編譯報錯(因爲基類方法私有)
        aSuper.f();
    }
}

初一看,這不是一樣麼,其實是不一樣,上述可以那是因爲調用方在基類裏面,當然可以調用內部的私有方法,如上情況只對基類內部私有, 當然也就不能調用,這裏就又引出一個問題,是不是聲明爲基類的私有方法,子類就無法進行重寫呢?根據我們上述打印的結果來看,理論上不可行,事實情況是可以的,通過內部類(後續會出文章詳細講解)來實現。

class Main {

    private void f() {
        System.out.println("Main.f");
    }

    class Inner extends Main {
        private void f() {
            System.out.println("Inner.f");
        }
    }

    public static void main(String args[]) {

        //內部類實例必須通過外部類實例創建
        Main outer = new Main();
        Inner inner = outer.new Inner();

        //內部類可以在內部訪問外部的所有成員(包括私有)
        inner.f();

        // 調用外部類方法
        outer = inner;
        outer.f();
    }
}

C#若明確需要重寫,那麼基類方法聲明爲虛有的virtual,子類通過ovverride關鍵字修飾方法達到重寫目的,若沒有這兩個關鍵字,和Java中一樣只是方法簽名一致,那麼說明編譯器會提醒是否通過new關鍵字來表明隱藏基類的方法

class Program
{
    public void F()
    {
        Console.WriteLine("Main.f");
    }

    static void Main(string[] args)
    {
        Program program = new Sub();
        program.F();

        Console.ReadKey();
    }
}

class Sub : Program
{
    public new void F()
    {
        Console.WriteLine("Sub.f");
    }
}

總結

Java和C#中的重寫區分度非常清晰,Java中只要方法簽名一致就可以達到重寫,不過建議通過註解方法來顯式聲明重寫以免引起不必要的問題,同時,即使基類方法私有,我們也可藉助於內部類來實現重寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章