JAVAプログラミング思想(3)-多重類(一)

15633 ワード

多重化クラス
コンビネーション構文
組み合わせは、オブジェクトリファレンスを新しいクラスに配置するだけです.たとえば、複数のStringオブジェクト、いくつかの基本タイプのデータ、および別のクラスのオブジェクトを持つオブジェクトが必要だとします.基本タイプ以外のオブジェクトの場合は、参照を新しいクラスに配置する必要がありますが、基本タイプデータを直接定義できます.
継承構文
継承はすべてのOOP言語とJava言語に不可欠な構成部分である.クラスを作成する場合、常に継承されます.したがって、他のクラスから継承することが明確に指定されていない限り、Javaの標準ルートクラスObiectから暗黙的に継承されます.
ベースクラスの初期化
クラスではなくベースクラスとエクスポートクラスの2つのクラスに関連しているため、エクスポートクラスで発生した結果オブジェクトを想像してみると、少し困惑します.
外部から見ると、ベースクラスと同じインタフェースを持つ新しいクラスのように、追加の方法やドメインがあるかもしれませんが、継承はベースクラスをコピーするインタフェースだけではありません.
エクスポートクラスのオブジェクトを作成すると、ベースクラスで直接作成したオブジェクトと同じベースクラスのサブオブジェクトが含まれます.両者の違いは、後者が外部から来ており、ベースクラスのサブオブジェクトがエクスポートクラスオブジェクトの内部にパッケージされていることである.
ベースクラスを初期化する唯一の方法は、コンストラクタでベースクラスコンストラクタを呼び出して初期化を実行することです.ベースクラスコンストラクタには、ベースクラスの初期化を実行するために必要なすべての知識と能力があります.
コンストラクション・プロシージャはベース・クラスから「外側」に拡散するため、ベース・クラスはクラス・コンストラクタがアクセスできるようになる前に初期化が完了します.表示されていないコンストラクタでも、コンパイラはベースクラスのコンストラクタを呼び出すデフォルトのコンストラクタを合成します.
エージェント
継承と組合せを除く第3の種類の関係は、エージェントである.
Javaは直接サポートしていません.これは継承と組み合わせの間の中庸の道である.
オブジェクトを構築するクラス(組合せのように)に配置するためですが、メンバーオブジェクトのすべてのメソッド(継承のように)も新しいクラスに暴露します.エージェントは、オブジェクトメンバーメソッドが露出する問題を解決します.
//: reusing/SpaceShipDelegation.java

public class SpaceShipDelegation {
  private String name;
  private SpaceShipControls controls =
    new SpaceShipControls();
  public SpaceShipDelegation(String name) {
    this.name = name;
  }
  // Delegated methods:
  public void back(int velocity) {
    controls.back(velocity);
  }
  public void down(int velocity) {
    controls.down(velocity);
  }
  public void forward(int velocity) {
    controls.forward(velocity);
  }
  public void left(int velocity) {
    controls.left(velocity);
  }
  public void right(int velocity) {
    controls.right(velocity);
  }
  public void turboBoost() {
    controls.turboBoost();
  }
  public void up(int velocity) {
    controls.up(velocity);
  }
  public static void main(String[] args) {
    SpaceShipDelegation protector =
      new SpaceShipDelegation("NSEA Protector");
    protector.forward(100);
  }
} ///:~

上記の方法は、下位のcontrolsオブジェクトにどのように渡され、そのインタフェースは継承されたインタフェースを使用するのと同じですが、エージェントは、露出したい方法を選択できるため、より多くの制御力を持つことができます.
組合せと継承の組み合わせ
コンパイラは初期話ベースクラスを強制し、コンストラクタの開始時にそうするように要求しますが、メンバーオブジェクトも初期化する必要があることを監督していません.そのため、に注意する必要があります.
クリーンアップが正しく行われていることを確認
JavaにはC++の解析関数はありません.解析関数は、オブジェクトが破棄されたときに自動的に呼び出される関数です.Javaでは、オブジェクトを破壊することなく忘れてしまう習慣があり、ゴミ回収器に必要なときにメモリを解放させるからです.
ただし、クラスはライフサイクル中に必要なクリーンアップアクティビティを実行する場合があります.ごみ回収器がいつ呼び出されるか、呼び出されるか分からない.そのため、何かを整理したい場合は、このことをするために特別な方法を表示的に作成し、クライアント・プログラマーがこの方法を呼び出さなければならないことを知っていることを確認する必要があります.
//: reusing/CADSystem.java
// Ensuring proper cleanup.
package reusing;
import static net.mindview.util.Print.*;

class Shape {
  Shape(int i) { print("Shape constructor"); }
  void dispose() { print("Shape dispose"); }
}

class Circle extends Shape {
  Circle(int i) {
    super(i);
    print("Drawing Circle");
  }
  void dispose() {
    print("Erasing Circle");
    super.dispose();
  }
}

class Triangle extends Shape {
  Triangle(int i) {
    super(i);
    print("Drawing Triangle");
  }
  void dispose() {
    print("Erasing Triangle");
    super.dispose();
  }
}

class Line extends Shape {
  private int start, end;
  Line(int start, int end) {
    super(start);
    this.start = start;
    this.end = end;
    print("Drawing Line: " + start + ", " + end);
  }
  void dispose() {
    print("Erasing Line: " + start + ", " + end);
    super.dispose();
  }
}

public class CADSystem extends Shape {
  private Circle c;
  private Triangle t;
  private Line[] lines = new Line[3];
  public CADSystem(int i) {
    super(i + 1);
    for(int j = 0; j < lines.length; j++)
      lines[j] = new Line(j, j*j);
    c = new Circle(1);
    t = new Triangle(1);
    print("Combined constructor");
  }
  public void dispose() {
    print("CADSystem.dispose()");
    // The order of cleanup is the reverse
    // of the order of initialization:
    t.dispose();
    c.dispose();
    for(int i = lines.length - 1; i >= 0; i--)
      lines[i].dispose();
    super.dispose();
  }
  public static void main(String[] args) {
    CADSystem x = new CADSystem(47);
    try {
      // Code and exception handling...
    } finally {
      x.dispose();
    }
  }
} /* Output: Shape constructor Shape constructor Drawing Line: 0, 0 Shape constructor Drawing Line: 1, 1 Shape constructor Drawing Line: 2, 4 Shape constructor Drawing Circle Shape constructor Drawing Triangle Combined constructor CADSystem.dispose() Erasing Triangle Shape dispose Erasing Circle Shape dispose Erasing Line: 2, 4 Shape dispose Erasing Line: 1, 1 Shape dispose Erasing Line: 0, 0 Shape dispose Shape dispose *///:~

各クラスはShapeのdispose()メソッドを上書きし,superを用いてメソッドのベースクラスバージョンを呼び出す.各クラスには、メモリに保存されていないものをオブジェクトが存在する前に復元するための独自のdispose()メソッドがあります.
キーワードtryは、次のブロック(括弧で囲まれた範囲)は、いわゆる保護領域であり、これは特殊な処理が必要であることを意味する.1つの特殊な処理は、tryブロックがどのように終了しても、保護領域後のfinally句のコードは常に実行される.ここでのfinally句は、「どんなことがあっても、必ずxのためにdispose()を呼び出す」ことを表す.
多くの場合、クリーンアップは問題ではなく、ゴミ回収器にこの動作を完了させるだけでよいが、自分で処理しなければならない場合は、より多くの努力と注意が必要であり、ゴミ回収器は私たちが望む順序で回収対象を取ることができず、任意の順序で回収することができる.だから、一番いい方法はメモリ以外に、ゴミ回収器に頼って何もしてはいけないことです.掃除が必要なら、自分の方法を書いたほうがいいです.
ネームマスク
Javaのベースクラスに複数のリロードされたメソッド名がある場合、エクスポートクラスでメソッド名を再定義しても、ベースクラスのバージョンはブロックされません(これはC++とは異なります).したがって、このレイヤまたはそのベースクラスでメソッドを定義しても、リロードメカニズムは正常に動作します.
//: reusing/Hide.java
// Overloading a base-class method name in a derived
// class does not hide the base-class versions.
import static net.mindview.util.Print.*;

class Homer {
  char doh(char c) {
    print("doh(char)");
    return 'd';
  }
  float doh(float f) {
    print("doh(float)");
    return 1.0f;
  }
}

class Milhouse {}

class Bart extends Homer {
  void doh(Milhouse m) {
    print("doh(Milhouse)");
  }
}

public class Hide {
  public static void main(String[] args) {
    Bart b = new Bart();
    b.doh(1);
    b.doh('x');
    b.doh(1.0f);
    b.doh(new Milhouse());
  }
} /* Output: doh(float) doh(char) doh(float) doh(Milhouse) *///:~

Bartには新しいリロード方法が導入されているが(C++でこの作業を完了するにはベースクラスメソッドをブロックする必要がある)、BartではHomerのすべてのリロード方法が使用可能である.ベースクラスと全く同じ特徴署名および戻りタイプを使用して同じ名前のメソッドを上書きするのは極めて一般的なことであるが、これも困惑させる(C++は許可していません)@Override注記は、キーワードではありませんが、キーワードとして理解できます.メソッドを上書きしたい場合は、この注記を選択して、上書きではなくリロードに注意しない場合は、コンパイラがエラーを報告します.このように@Override注記は、リロードしたくないときに意外にリロードされることを防止します.