ProcessingでInnerClassの話とか

追記 -- 2015/06/18


なるほどなーって感じです
この辺はスゴく気を付けて実装しよう…




先日こんなコードに出会いました



Windows上で実行した結果もよく分からんが, iOS上で実行した結果と食い違ってるので余計に分からん…

どっちが合ってて, どっちが間違ってるのかハッキリさせたいし, どっちが合ってるにしても何でそうなるのか知りたかったので色々と調べてみたことを纏めておきます


結論として, よく分からんので誰か教えてください…





以下ではWindows 8.1 + Processing 2.2.1で行っています

class A{
  private void a(){
    println("a");
  }
}

class B extends A{
  void a(){
    println("b");
  }
}

void setup(){
  B c = new B();
  A d = new B();
  c.a();
  d.a();
}

実行結果は

b
a

となります


個人的に分からなかった箇所は

  • dはどのクラスのインスタンスの参照なのか
  • ClassAでprivateなa()がsetup内で呼べるのは何故か
  • ClassAでprivateなa()はClassBでオーバーライドされているのか
  • d.a()でaと表示されるのは何故か

の4つです


まずdはどのクラスのインスタンスの参照なのかという話ですが, instanceofで調べてみると

class A{
  private void a(){
    println("a");
  }
}

class B extends A{
  void a(){
    println("b");
  }
}

void setup(){
  B c = new B();
  A d = new B();
  A e = new A();
  if(c instanceof A){
    println("ClassA");
  }
  if(c instanceof B){
    println("ClassB");
  }
  if(d instanceof A){
    println("ClassA");
  }
  if(d instanceof B){
    println("ClassB");
  }
  if(e instanceof A){
    println("ClassA");
  }
  if(e instanceof B){
    println("ClassB");
  }
}

この結果は

ClassA
ClassB
ClassA
ClassB
ClassA

となるので, ClassBのようです


次にClassAでprivateなa()がsetup内で呼べるのは何故か, これはClassAやClassBがInnerClassだからです

その辺はProcessingの話になるので割愛しますが, この辺を見れば分かるような…d.hatena.ne.jp


InnerClassとして定義されているのであれば, 外部クラスから内部クラスのprivateなメソッドにアクセス出来るはずです


次にClassAでprivateなa()はClassBでオーバーライドされているのか

class A{
  private void a(){
    println("a");
  }
}

class B extends A{
  @Override
  void a(){
    println("b");
  }
}

void setup(){
  B c = new B();
  A d = new B();
  c.a();
  d.a();
}

オーバーライドアノテーションを付けた場合

The method a() of type sketch_.....B must override or implement a supertype method

と表示されるため, ClassBから見るとprivateは効いてるようです


最後にd.a()でaと表示されるのは何故か
今まで調べた情報から考えるとdはClassBのインスタンスの参照のようですが, ClassAのa()が実行されています

さっぱり分からないのでこんな感じで書いて実行してみました

class A{
  void a(){
    println("a");
  }
  void b(){
    println("b");
  }
}

class B extends A{
  void a(){
    println("_a");
  }
  void c(){
    println("c");
  }
}

void setup(){
  B i = new B();
  A j = new B();
  i.a();
  j.a();
  i.b();
  j.b();
  i.c();
  j.c();
}

すると最後のj.c()でエラーを吐きました

しかし上でも書いたとおり

if( j instanceof B){
  println("ClassB");
}

はしっかりとClassBと表示されるので, jはClassBの参照っぽいです

ClassBの参照っぽいのにClassAになくてClassBにあるメソッドを呼び出せない感じ


次にこんなコードを書いて実行してみました

class A{}
class B extends A{}

void setup(){
  B i = new B();
  A j = new B();
  A k = new A();
  
  ArrayList<A> a = new ArrayList<A>();
  ArrayList<B> b = new ArrayList<B>();
  
  a.add(i);
  a.add(j);
  a.add(k);
  
  b.add(i);
  b.add(j);
  b.add(k);
}

するとb.add(j)でエラーを吐きます

これらのことから纏めると, A j = new B()はinstanceofではClassBでもtrueを返しますが, 実際の振る舞いとしてはClassAとして存在しているようです

でも継承先でオーバーライドとかすると, しっかりオーバーライドしたものを実行したり…



話を戻しますが

class A{
  private void a(){
    println("a");
  }
}

class B extends A{
  void a(){
    println("b");
  }
}

void setup(){
  B c = new B();
  A d = new B();
  c.a();
  d.a();
}

この場合, dはinstanceofするとClassBで, でもメソッドの有無を確認するときはClassAで, しかしながらメソッドを実行するときは普通はClassBで, でもprivateなときはClassAみたいな?






致命的に頭が悪いのでしっかり勉強します…
何か分かったらまた追記していきます…