いけむランド

はてダからやってきました

続・private についての疑問

主にブクマにいただいたコメントへの回答です。

t_yano ん?どうなんだろ。静的コンパイラ言語としては、実行バイナリを作るコンパイル段階でそういうエラーを出すことはOKだと思うんだけど.../shuyoに説明されて、他人のclassオーバーライドしてこれが出たら微妙なだとおもた
yojik プログラム的に隠蔽してるからといって、開発者からも分からなくする必要は無いと思います。むしろコンパイル時は積極的に情報開示するべき。
shuji_w6e えっ? アクセス制御であって、スーパークラスの契約の一部(private field)が伝わる事は良い事だと思う

コメントをいただいて気づいたのですが、

  • アクセス制御という意味での private
  • その存在自体が外部に漏れてはいけないという意味での private

なのかで話が変わってきますね。自分としては shuyo さんが仰っていたのは後者の意味で、Java における private は前者の意味だと受け取りました。Java では (仕様で決まっている) クラスファイルを解析したり、(標準ライブラリに含まれる) リフレクションを使えば、private なメンバの存在も容易に知れるのだから、存在を private にする意味もそれほどないかなと思います。

アクセス制御の private と考えれば、存在自体は public でもいいことになるから、(ご指摘の通り) コンパイル段階で「アクセスできない情報にアクセスしようとしている」というエラーを出すのはむしろ当然のように思えます。

Nagise コンパイルしてから親をprivateにした場合は実行時例外だったと思うのだけど。後で確認する
tomorrowkey ほんとにアクセスできた。なんてこったい
Akineko 試してみたら実行時例外(IllegalAccessError)でました。[version:1.6.0_14]

とりあえず手元の Windows に 5 と 6 を入れて、5 のコンパイラで生成したクラスを 5 と 6 の実行環境でそれぞれ実行させてみたところ、5 ではアクセスできて、6 では java.lang.IllegalAccessError が投げられました。*1

% /usr/local/java5/bin/java -version
java version "1.5.0_20"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_20-b02)
Java HotSpot(TM) Client VM (build 1.5.0_20-b02, mixed mode, sharing)
% /usr/local/java6/bin/java -version
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03)
Java HotSpot(TM) Client VM (build 14.1-b02, mixed mode, sharing)
public class Parent
{
  public int val; // Child をコンパイル後に private に変更 → Parent だけ再コンパイルして整合性を失わせる。
  public Parent(int val) { this.val = val; }
}
public class Child extends Parent
{
  public Child(int val) { super(val); }

  static public void main(String[] args)
  {
    Child c = new Child(2);
    System.out.println(c.val);
  }
}
% /usr/local/java5/bin/java Child
2
% /usr/local/java6/bin/java Child
Exception in thread "main" java.lang.IllegalAccessError: tried to access field Parent.val from class Child
        at Child.main(Child.java:8)


さすがにこれ以上は JVM の実装のソースを読まないとわからないような気がします...。

*1:6 のコンパイラで -target 1.5 を指定して生成しても同様の結果でした。