過去の日記

2005-11-04 義捐金, java nested class [長年日記]

nested class [tech][java]

ネストクラス・インナークラスに関して勉強中ですが、
下記のようなルール
(略)
などあるようですが、そういうものかと頭では思えても、
・なぜそのようなルールがあるのか、
・このような仕組みは、実際の開発ではどのように
使われているのか、
といったことが見えてこないので、ぴんときません。

そこで、このネストクラス・インナークラスの仕組みがよく
分かるようになるサイト等をご存知の方がおりましたら
ご教授下さいますようお願いします。
http://www.hatena.ne.jp/1131095872


Java言語仕様(Java Language Specification)

http://java.sun.com/docs/books/jls/second_edition/html/jTOC.doc.html

に書いてあることなので、"why?"を突き詰めていこうとすると、仕様を策定した偉い人が書いた他の解説にあたるなり、直に訊くなりしないといけないのだけど。
ま、憶測することぐらいはできるわけだが。


以下、クラス名は以下の凡例で示す。

class TopLevelClass {
   class MemberClass {
   }
   
   static class StaticMemberClass {
   }
}


(1)非staticなネストクラスは、staticなメンバ・フィールドを持てない

実はこれは正しくない。

Inner classes may not declare static members, unless they are compile-time constant fields.
http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#262890

とあり、コンパイル時で静的になるフィールドならばstaticなメンバは許される。簡単な例となるコードも載っている。
これが何故か? と問われると困るが「必要が無いから」といっていいだろう。後で示そう。


(2)非staticなネストクラスのオブジェクトは、外側のオブジェクトを生成してから生成する

この性質はある1つの事実を暗に示している。非staticなネストクラス――つまり、内部クラス――は、暗黙で外側のクラスのインスタンスへの参照を持っている、ということだ。
この暗黙の参照は、TopLevelClass.this という書き方で使用することができる。

class TopLevelClass {
   class MemberClass {
      void notStaticMethid() {
         TopLevelClass t = TopLevelClass.this;  //←ここ。
         t.anMethodOfTopLevelClass();
      }
   }
   
   void anMethodOfTopLevelClass() {
   }
}

ちなみに、単にthisと書くとMemberClassのインスタンスを指してしまう(当たり前ではあるが)。
この暗黙の参照があればこそ*1、TopLevelClassのインスタンスメソッド(非staticなメソッド)やインスタンスフィールドがMemberClassの中で利用できる。逆に言うと、TopLevelClassのインスタンスメソッドやインスタンスフィールドを使用する必要がなければ static を付けて単なるネストしたクラスにすればよい。
さて、MemberClassのインスタンスは(暗黙で)TopLevelClassのインスタンスを必ず参照していることを納得したとしよう。ということは、MemberClassのインスタンスを生成する時にTopLevelClassのインスタンスが必要なわけだ。
TopLevelClassのインスタンスメソッドの中で new MemberClass() の様にインスタンスを生成する場合は話が簡単だ。暗黙で this(=TopLevelClassのインスタンス)を渡しているだけの話。
TopLevelClassのインスタンスメソッドの外――「TopLevelClassの外」という意味以外に「TopLevelClassのクラスメソッドなど」も含まれることに注意――では、

TopLevelClass t = new TopLevelClass;
TopLevelClass.MemberClass m = t.new MemberClass();

という構文になる。t.new という形で、TopLevelClassのインスタンスに結びつけられる(結びつけられなければならない)ということが判る。


この事実の裏が、

(4)staticなメソッド内で、非staticなネストクラスのオブジェクトは生成できない

の理解を可能にする。
「非staticなネストクラスのオブジェクトは生成できない」は厳密には正しくない。
new MemberClass() という形での生成ができないだけである。staticなメソッド内では this という指示詞が使えないことを思い出して欲しい。new MemberClass() と書いた時に暗黙に渡している this が無いから、この形での生成ができないのだ。
(2)で先に述べてしまったが、

TopLevelClass t = new TopLevelClass;
TopLevelClass.MemberClass m = t.new MemberClass();

という形でなら生成できるので"厳密には正しくない"と言った。


(1)で残してきたことを説明しよう。
内部クラスがstaticな(かつ finalでない)メンバを持っても特に意味が無い。
TopLevelClass.thisという形で外側のクラスのメンバを参照できるし、外側のクラスのstaticメンバも――例え private が指定してあっても――使えるからである。それで十分であろう。
「内部クラスのstaticなメンバが外側のクラスインスタンス毎に用意される」という誤解を招くことを懸念して、内部クラスでのstaticなメンバを禁止したのではないだろうか?


(3)staticなネストクラスは、自分を含むクラスに付随し、外側のオブジェクトがなくとも使える

これも(2)の裏であろう。
内部クラスが外側のクラスのインスタンス無しには生成できないのと違い、staticを指定したクラスはその制約から免れられる。
ただそれだけの話。


(5)メソッド内で定義されたインナークラスからアクセスできるのは
*外側のメソッドのfinalな引数・変数
*メソッドを持つクラスのフィールド

これはちょっと難しい。いわゆる無名内部クラス。

class TopLevelClass {
   class MemberClass {
      void anMethidOfMemberClass() {
         int a = 100;
         final int b = 100;

         TopLevelClass t = new TopLevelClass() {
            public void inheritedMethod() {
               int m = a; // NG
               int n = b; // OK
            }
         }
      }
   }
}

という様なパターン。Other は interface や abstract class のことが多いだろう。
インスタンス t の inheritedMethod が呼ばれるのが、anMethidOfMemberClass() の実行が終わった後だということを想定すると、int m = a; が駄目だろうなぁ、とは思う程度で、これはちゃんと解説できない。


・このような仕組みは、実際の開発ではどのように使われているのか、

これはSDKのソースを参考にするのも1つの手段だと思う。

java.util.LinkedList

のlistIterator()メソッドあたりがいいのでは?

    public ListIterator listIterator(int index) {
        return new ListItr(index);
    }

    private class ListItr implements ListIterator {
        :
        :
    }

こんな感じ。
listIteratorメソッドが返すListIteratorの正体は、privateなインナークラスである。
privateなインナークラスということは、その実装は LinkedList の中に隠蔽できる。
それはとりもなおさず、LinkedList の内部データの隠蔽性を維持できる、ということでもある。


もしインナークラスが無いことを想像してみよう。listIteratorメソッドが返すインスタンスを LinkedList の外のクラスとして実装しなくてはならなくなる。そうすると、LikedList はそのクラスに対して内部データを何らかの形で公開しなければならない。それでは結局 LikedList の内部データが隠蔽できない=不正にいじられる可能性を生む。

一番きれい [hatena]

どのメーカーのどの機種が一番きれいに撮れるでしょうか?
http://www.hatena.ne.jp/1131061683

「きれい」を定義してください。
クライアントから「とにかくかっこよく」と依頼があったら、どう思うのかな? 「わかりました」って言って作れるのかな?

寄付金控除の対象に成る団体に義援金を という選択肢をもっと広めよう [etc]

「被災地に古着等を送るのは迷惑です」というのをもっと啓蒙する必要はありそう。「善意だから良い事なんだ。それに文句をつけるなんて酷い」という反応を起こさないように狡猾に


赤十字などの寄付金控除の対象に成る団体に義援金を送るのが最もバランスの良い支援の方法。
http://www.otsune.com/diary/2005/11/03/3.html#200511033

一票。
ただし、「狡猾に」かつ「短くまとめて」。
本文が長いとそれだけで読んでもらえない


[R30]: 寄付する前に立ち止まれ

はちょっと長すぎると思うので。


……人任せかい? と言われそうだな。

*1 というのは厳密には正しく無いはずだ。もっと複雑な仕掛けがあったはずで、それはVMの仕様書を読み解く必要があったと記憶している。のだけど、そこまで理解する必要は、javaプログラムを書く人には必要はないはずだ。