2017-09-15 47 views
1

下面的代碼產生當能見度改爲private重寫方法和變量 - 不一致的行爲

這是可以理解的,但奇怪的是重寫的變量不會產生任何錯誤彙編覆蓋的方法getName(),時間誤差。

class Base { 

    public String getName() { 
     return "Base"; 
    } 

    public String className = "Base"; 
} 

class Derived extends Base { 
    private String getName() { //Not compiling 
     return "derived"; 
    } 

    private String className = "Derived"; //Compiling successfully 
} 

public class Test{ 
    public static void main(String[] args) { 
    System.out.println((new Derived()).className);// Gives compilation error 
    } 

有人能幫我理解爲什麼會發生這種情況嗎?

雖然我們試圖訪問main()編譯私有變量失敗,但在方法中自當我從公衆減少接入類型私人它編譯成功它應該有失敗有作爲

+0

你不能覆蓋方法並改變它的簽名修飾符。 – matoni

+2

@matoni,當然可以。例如,不是「public」>「private」。 – rorschach

+0

@matoni根據Liskov Substitution原則,訪問修飾符比基類中的更少,這是錯誤的。但是如果你願意的話,你可以在子類中使它更公開。 – Matthias

回答

3
重寫

用較弱的訪問改性劑的方法是通過the standard (§8.4.8.3)禁止:

接入改性劑(6.6節)壓倒一切的或隱藏方法必須提供至少儘可能多訪問的重寫或隱藏方法,如下所示:

  • 如果重寫或隱藏的方法是公共的,則超控或隱藏方法必須是公共的;否則,會發生編譯時錯誤。

  • 如果重寫或隱藏的方法受到保護,則重寫或隱藏方法必須受到保護或公開;否則,會發生編譯時錯誤。

  • 如果重寫或隱藏方法具有默認(包)訪問權,則重寫或隱藏方法不能是私有的;否則,會發生編譯時錯誤。

這確保了由基類提供的任何方法也可以在相同的上下文中派生類被調用。

變量不能被覆蓋。 Base.classNameDerived.className是兩個不同的變量。因此,在Derived中使用相同名稱和不同訪問修飾符的變量是完全有效的。

I.e.這段代碼將打印false

class Base{ 
    public String str = "hello"; 
} 

class Derived extends Base{ 
    private String str = "whatever"; 

    public Derived(){ 
     super.str = "abc"; 
     str = "def"; 
    } 

    void foo(){ 
     System.out.println(str.equals(super.str)); 
    } 
} 

public static void main(String[] args){ 
    new Derived().foo(); 
} 

相關JLS-部分:

Field declarations (§8.3)

字段聲明的§6.3和6.4節指定的範圍和陰影。

如果類聲明瞭一個具有特定名稱的字段,那麼該字段的聲明被稱爲隱藏在超類和該類的超接口中具有相同名稱的字段的任何和所有可訪問的聲明。

在這方面,隱藏字段不同於隱藏方法(第8.4.8.3節),因爲在字段隱藏中在靜態和非靜態字段之間沒有區別,而靜態和非靜態之間有區別方法隱藏中的方法。

如果隱藏字段是靜態的,則可以使用限定名稱(第6.5.6.2節)或使用包含關鍵字super(第15.11.2節)的字段訪問表達式或轉換爲超類的字段訪問表達式來訪問類型。

在這方面,隱藏字段類似於隱藏方法。

如果一個字段聲明隱藏了另一個字段的聲明,則這兩個字段不必具有相同的類型。

而且Shadowing (§6.4.1)

字段或名爲N的陰影,整個d的範圍形式參數的聲明d,名爲N的任何其他變量的聲明是在範圍上點在哪裏d發生。

0

將超類的方法重寫到子類時,訪問級別可以保持相同或應該更寬/更寬(即增加子類中重寫方法的訪問可見性)。

因此,如果您的基類方法是公共的,那麼您不能將該方法重寫爲private或protected。

1

無法覆蓋具有更多限制性訪問說明符的方法(例如,當超類中的方法爲public時,爲private)。如果這是可能的,你就可以做奇怪的事情,如調用一個private方法不應該訪問:

Derived object1 = new Derived(); 

// Will give an error, because getName() is private 
String name1 = object1.getName(); 

Base object2 = new Derived(); 

// Should this be possible because getName() is public in Base? 
// (Note that object2 is of type Base). 
// But that would be strange, because the method is overridden 
// in Derived, so we would be calling a private method here that 
// should not be accessible from the outside! 
String name2 = object2.getName(); 
+0

我已編輯代碼,如果你可以看到,而我們試圖訪問私有變量編譯失敗,但在方法它自我,當我減少訪問類型從公共私人它編譯成功,它應該也失敗了。 –

+0

在子類中的成員變量做不會覆蓋超類中的成員變量,它不會像方法那樣工作,只會得到兩個成員變量:一個在超類中,一個在子類中,子類中的變量隱藏(不覆蓋)在這個超類中 – Jesper

+0

這隻回答問題的第一部分,但完全忽略了關於「覆蓋」v事實上,這是不可能的。 – Paul

2

你不能override場,但只是hide它。 這意味着您只需創建具有相同名稱的新變量。

從JLS Field declaration

如果類聲明的字段具有特定名稱,那麼該字段的聲明說,隱藏字段的任何和所有可訪問的聲明與超和超級同名的類。