Effective Javaを読むチャレンジ-項目22その2-

前回の項目22のつづきです。4つのネストされたクラスの残り、無名クラスとローカルクラスの話です。

無名クラス

無名クラスについて、本書ではびっしりとその特徴と使用方法を羅列しています。まずは無名クラスの特徴から箇条書きでまとめます。

  1. 無名クラスは名前を持たない
  2. 無名クラスはエンクロージングクラスのメンバーではない
  3. 無名クラスは使用される時点で宣言と同時にインスタンス化される
  4. 無名クラスは式が許されている場所であれば、コードのどの場所でも許されている
  5. staticの文脈内で書かれたならば、無名クラスはエンクロージングインスタンスを持つ
  6. しかし、staticの文脈内で書かれたとしても、staticのメンバを持つことはできない
  7. 無名クラスが宣言された箇所以外で、無名クラスのインスタンス化はできない
  8. instanceof検査などのクラスの名前を指定する必要がある処理はできない
  9. 複数のインタフェースを実装したり、クラスを拡張すると同時にインタフェースを実装するように宣言できない
  10. 無名クラスのクライアントは、無名クラスがスーパータイプから継承しているメソッド以外のメソッドを呼び出すことができない
  11. 長すぎる無名クラスはコードの可読性を下げる。10行以下であるべき。

最後の項目以外は、Java言語仕様とJVM仕様の内容をまとめたもののようです。次に、無名クラスの使用方法です。

  1. 関数オブジェクトを生成するとき
  2. Thread、Runnable、TimerTaskなどのプロセスオブジェクトを生成するとき
  3. staticファクトリーメソッド内での使用

2段落の間にこれだけのことを一気にわっと言われても・・・とりあえず1つ1つ見ていきますか。

無名クラスは名前を持たない

もう少し詳しく言うと、無名クラスは名前を持たない(静的ではない)内部クラスです。

無名クラスはエンクロージングクラスのメンバーではない

JVMの内部的に、内部クラスが外側のクラスのメンバでない場合に0が設定される属性があるようで、トップレベルクラス、インタフェース、無名クラス、ローカルクラスが0になるようです・・・たぶんこのことを言っているのかと思います。

無名クラスは使用される時点で宣言と同時にインスタンス化される

無名クラスの宣言は「new 型名(引数){クラス本体}」のような、インスタンスを生成するための式にクラス本体をくっつけた形をした記述をします。この「{クラス本体}」で終わっていることが重要です。また、型名には通常、インタフェースや継承可能なクラス名を記述する必要があります。無名クラスは、型名に記述したクラスやインタフェースの匿名のサブクラスとなるため、継承できないクラスの無名クラスは宣言できません。

無名クラスは式が許されている場所であれば、コードのどの場所でも許されている

無名クラスは上記のとおりインスタンスを生成するための式ですので、他の式と同じように使えます。たとえば代入の右辺の式やメソッド参照、メソッド呼び出しなどです。

staticの文脈内で書かれたならば、無名クラスはエンクロージングインスタンスを持つ

おそらくこういうことかと思います。

前回、非staticメンバークラスがエンクロージングインスタンスに暗黙的に関連付けられることを確かめるために書いたサンプルとやりたいことはほぼ同じです。結果も同じです。つまり、staticの文脈内で書かれた無名クラスもエンクロージングインスタンスに関連付けられているということです。

しかし、staticの文脈内で書かれたとしても、staticのメンバを持つことはできない

main内の無名クラスにstaticフィールドを追加すると、「The field フィールド名 cannot be declared static in a non-static inner type, unless initialized with a constant expression」というコンパイルエラーになります。

instanceof検査などのクラスの名前を指定する必要がある処理はできない

「b instanceof new B(){}」といったことはできません。

複数のインタフェースを実装したり、クラスを拡張すると同時にインタフェースを実装するように宣言できない

class宣言のようには書けないということです。

無名クラスのクライアントは、無名クラスがスーパータイプから継承しているメソッド以外のメソッドを呼び出すことができない

無名クラスは匿名のサブクラスですが、それをクライアントが使おうとすると、自然と無名クラスのスーパークラスでインスタンスを代入させることになります。

ということは、スーパークラスにないメソッドを呼び出すことはできないということになります。

無名クラスの使用方法について、関数オブジェクトは項目21の具象戦略のサンプルで出てきたような使い方です。2番目のプロセスオブジェクトは上記のサンプルのようにこの場でだけrun()メソッドを実装したいとかに使います。3番目のstaticファクトリーメソッド内での使用というのは、staticファクトリーメソッドの戻り値を戻り値の型の無名クラス(要はサブクラス)としてその場で生成する場合のことです。

エンクロージングクラスのメンバや無名クラスを使用したメソッド内のローカル変数も簡単に参照できるので、無名クラスを使う人もいると思います。いちいちクラスを増やしてコンストラクタやアクセサ用意しなくていいですから。そのあたりは現場のルールということになっちゃうんですかね・・・前の項目に出てきたと思いますが、無名クラスは毎回インスタンスを生成するので、そういう面でも注意する必要があります。あまり多用して欲しくないかな、という感じです。

ローカルクラス

ローカルクラスはメソッド内で定義するクラスです。ローカルクラスは他の3つのクラスの特性をいくつか併せ持っています。これまでの3つの話を読んでいれば、本書の説明は難しくないです。

本書の第4章、クラスとインタフェースの話は以上です。1回読んだだけでは消化不良、というよりそもそも喉元を通らないぐらい難しい項目ばかりでした。ここから先は個人的にほぼ未踏の地です。どうにか読んでいきたいと思います。

広告
  • LINEで送る