accessibility

Irenkaでそういや可視性について放置していたので、まじめに調べてみた。

これでメソッドfはOKでメソッドgがNGなのはなんで?

public class N{
    class A{
        private int a;
    }
    class B extends A{
        void f(A ar){
            System.out.println(ar.a);//OK
        }
        void g(){
            System.out.println(a);//コンパイルエラー
        }
    }
}
2007-12-06

以降、Java言語仕様第3版から引用。

まず、ar.a (N.A.a) が参照可能な理由。

  • A member (class, interface, field, or method) of a reference (class, interface, or array) type or a constructor of a class type is accessible only if the type is accessible and the member or constructor is declared to permit access:
    • ...
    • Otherwise, if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.
6.6.1 Determining Accessibility

つまり、メンバクラスN.BはトップレベルクラスNのスコープの中にいるので、N.A.aはprivateでもN.Bから参照可能です*1

ついでに、メンバのスコープの定義。

The scope of a declaration of a member m declared in or inherited by a class type C is the entire body of C, including any nested type declarations.

6.3 Scope of a Declaration

次に、b (N.B.a) が参照不可能な理由。

A private class member or constructor is accessible only within the body of the top level class (§7.6) that encloses the declaration of the member or constructor. It is not inherited by subclasses. In the example:

6.6.8 Example: private Fields, Methods, and Constructors

こっちはN.B.aとしてアクセスしようとしているので、N.A.aがサブクラスに継承されておらず、発見できない感じです。((A) this).aならもちろんOK。

要は、やってることは下と同じです。

public class N {
    
    public class A {
        private int a;
    }

    public class B extends A {}

    public static void main(String[] args) {
        N n = new N();
        A a = n.new A();
        B b = n.new B();
        System.out.println(a.a); // ok
        System.out.println(b.a); // NG
        System.out.println(((A) b).a); // ok
    }
}

*1:ものすごく違和感はありますが…