2017-09-15 6 views
1

がオーバーライドされたメソッドgetName()、視界がprivateオーバーライドメソッドと変数 - コードに続いて一貫性のない振る舞い

に変更されたときにこれは理解するが、妙にオーバーライドされた変数は、エラーを生成しないで時エラーをコンパイル生成します。

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)がオーバーライドさと少なくとも同じくらいのアクセスを提供しなければならない、または

    オーバーライドまたは非表示のメソッドがpublicの場合、オーバーライドまたは非表示のメソッドはpublicでなければなりません。それ以外の場合は、コンパイル時エラーが発生します。

  • オーバーライドされたメソッドまたは隠されたメソッドが保護されている場合は、オーバーライドまたは隠蔽のメソッドを保護またはパブリックにする必要があります。それ以外の場合は、コンパイル時エラーが発生します。

  • オーバーライドまたは非表示のメソッドにデフォルト(パッケージ)アクセスがある場合、オーバーライドまたは非表示のメソッドはプライベートである必要はありません。それ以外の場合は、コンパイル時エラーが発生します。

これは、基底クラスによって提供される方法はまた、同じコンテキスト内の派生クラスで呼び出すことができることを確実にします。

変数を上書きすることはできません。 Base.classNameおよびDerived.classNameは、2つの異なる変数である。したがって、同じ名前の別のアクセス修飾子を持つ変数を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.8.3)。フィールド隠蔽の静的フィールドと非静的フィールドの区別はなく、静的と非静的の区別がありますメソッド隠蔽のメソッド。

静的な場合は修飾名(§6.5.6.2)を使用するか、キーワードsuper(15.11.2)またはスーパークラスへのキャストを含むフィールドアクセス式を使用して非表示フィールドにアクセスできますタイプ。

この点で、フィールドの非表示はメソッドの非表示と似ています。

フィールド宣言が別のフィールドの宣言を隠す場合、2つのフィールドの型が同じである必要はありません。

そしてShadowing (§6.4.1)

宣言フィールドのDまたは影N名前仮パラメータ、Dの範囲を通して、ここの時点でスコープ内にあるNという名前の他の変数の宣言dが発生する。

0

スーパークラスのメソッドをサブクラスにオーバーライドしても、アクセスレベルは同じに保つことができます。または、より広い/広い(つまり、サブクラスのオーバーライドメソッドのアクセスの可視性を高める)必要があります。

したがって、基本クラスのメソッドがpublicであれば、そのメソッドを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

私は編集しましたあなたが見ることができれば、私たちはプライベート変数のコンパイルにアクセスしようとしている間に失敗しますが、メソッドでは、アクセスタイプをpublicからprivateに減らしたときにコンパイルを成功させることができます。 –

+0

サブクラスのメンバー変数doスーパークラスのメンバ変数をオーバーライドしません。メソッドと同じように動作しません。スーパークラスとサブクラスの2つのメンバ変数を取得します。サブクラスのメンバ変数は、オーバーライドしません – Jesper

+0

これは質問の最初の部分にのみ回答しますが、「上書き」に関する部分は完全に無視します。v実際には不可能である。 – Paul

2

することはできませんoverrideフィールドが、ちょうどhideそれ。 つまり、同じ名前の新しい変数を作成するだけです。クラスは、特定の名前を持つフィールドを宣言した場合JLS Field declaration

から

は、そのフィールドの宣言がスーパークラス、およびスーパーインタフェースで同じ名前のフィールドのいずれかと、アクセス可能なすべての宣言を非表示にすると言われていますクラスの弱いアクセス修飾子を持つメソッドをオーバーライド