戻る

継承

   本章では、オブジェクト指向プログラミングの特色の2つ目である継承を取り上げる。継承は既存のクラスを再利用する手段を提供する。
   これから継承を説明するにあたり、次のようなUme(梅)というクラスを考える。(まで、この部分には継承は登場しない)

   class Ume {
     …
   }

   クラスUmeは「梅」というランチを表現するクラスである。
  「梅」は3種類の料理から好きなものを選択出来るようになっている。その3種類の料理をdishesという配列で表現する。

   class Ume {
    String dishes[ ] = {
     "ハンバーグ";
     "ステーキ";
     ”魚";
    };
      …
   }

   dishes[0]ならハンバーグ、dishes[1]ならステーキ、dishes[2]なら魚が選択される。
  お客さんがどの料理を選択したかをselectというフィールドで表現する。

   class Ume {
    int select = 0;
      …
   }

   selectの初期値は0である。もしお客さんがステーキを注文したなら、select=1でdishes[select]がステーキを表すことになる。
   次にお客さんの注文を処理するためのメソッドorderを用意する。orderは引数を1つ取る。

   void order(int n) {
     …
   }

   引数のnは、お客さんがどの料理を注文したかを表す。メソッドorderは、nの値をselectに代入する。

   void order(int n) {
    select = n; ・・・・・注文はn
   }

   ただし、nは0以上2以下の整数でなければならない。(料理の種類は0以上2以下)
  そこで、もしこの範囲外の整数が入力されたら、0を選択する事にする。最終的なメソッドorderのコードは次のようになる。

   void order(int n) {
    if(n<0 || n>2) ・・・・・nが範囲外なら
     select = 0; ・・・・・0を注文
    else
     select = n; ・・・・・正しい注文
   }

   クラスUmeは、eatというメソッドも持っている。これは、注文された料理を出力するものである。

   void eat( ) {
    System.out.println("料理=" + dishes[select]);
   }

   以上で、クラスUmeが完成である。次にその全リストを示す。

   class Ume {
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    void order(int n) {
     if(n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( );
     System.out.println("料理=" + dishes[select]);
    }
   }

   これで、準備が整った。次に継承を使う事にする。

親クラスと子クラス

   javaは、あるクラスを元にして新しいクラスを作る事が出来る。これを継承という。
  このとき元になるクラスを親クラスあるいは、基本クラス、ベースクラス、スーパークラスなどという。(ここでは、親クラスという用語を使う)
  また、親クラスを継承して作られたクラスを子クラスあるいは、派生クラス、サブクラスなどという。(ここでは、子クラスという用語を使う)

   ※補:オブジェクト指向プログラミングでは、継承関係を図式化するとき、子クラスから親クラスに向かう矢印を書く。
       親クラスから子クラスへの矢印ではない事に注意。

Extends

   例として前々項で示したクラスUmeを継承してTake(竹)というクラスを作成する。
   Aというクラスを継承してBというクラスを作るにはextendsというキーワードを使って次のように宣言する。

   class B extends A ・・・・・BはAを継承する

   したがってクラスTakeのクラス宣言は次のようになる。

   class Take extends Ume {
   }

   現在、クラスTakeは空っぽでメンバーが定義されていないように見える。しかし、子クラスは、親クラスの全てのメンバーを継承する。
  したがって、Takeは空のクラスのように見えるが、Umeのフィールドとメソッドを持つことになる。
  その事を確かめるため、次のサンプルを試す。

   サンプル:Extends.java

   //
   //Extends.java---子クラスを作る
   //
   class Ume { ・・・・親クラス
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    void order(int n) {
     if(n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( ) {
     System.out.println("料理=" + dishes[select]);
    }
   }

   class Take extends Ume { ・・・・Takeのクラスは空
   }

   public class Extends {
    public static void main(String[ ] args) {
     Take t = new Take( );
     t.order(1);
     t.eat( );
    }
   }

   このmainメソッドに注目する。まず、tというクラスのTakeのインスタンスを生成している。

   Take t = new Take( ); ・・・・tはクラスTakeのインスタンス

   そして、orderというメソッドを呼び出している。Takeには、メソッドorderは定義されていない。
  しかし、親クラスのUmeにはorderがあるので、Takeでもorderを使う事が出来る。

   t.order(1); ・・・・料理の1を注文する

   これで料理の1が注文された事になる。料理の1はステーキであった。
  したがって、次のeatの呼び出しにより、ステーキが表示されるはずである。

   t.eat( );

   ちなみにこのeatもTakeのものではない。親クラスのUmeで定義されたものである。次にこのサンプルの実行例を示す。

   $ java Extends
   料理=ステーキ
   $ _

   空っぽのTakeでもorder、eatのメソッドが使えている事が分かる。

メンバーの追加

   子クラスは、親クラスの全てのメンバーを継承する。
  だからといって子クラスを空っぽのままにしていたのでは「子クラス=親クラス」となって、わざわざ子クラスを作る必要は無い。
   もちろん子クラスでもメンバーを定義する事は出来る。
  その場合、子クラスは「親クラスのメンバー+追加メンバー」の両方のメンバーを持つことになる。

   子クラスのメンバー = 親クラスのメンバー + 追加メンバー

   実際、子クラスTakeにメンバーを追加してみる。ランチ「竹」は、ランチ「梅」よりも少々高級である。何が高級かというと、スープがついて来る。
  スープも3種類の中から選ぶ事が出来る。そこで、スープの種類を示す配列soupをメンバーとして追加する。

   class Take extends Ume{
    String soup[ ] = {
     "コンソメ",
     "ポタージュ",
     "カボチャスープ",
    };
      …
   }

   お客さんがどのスープを注文したかを示すため、select2というフィールドを追加する。

   class Take extends Ume [
    int select2 = 0;
      …
   }

   お客さんの注文を処理する親クラスのorderは、引数が1つで、主料理の選択しか出来なかった。

   void order(int n) ←引数は1つ

   Takeクラスでは、主料理とスープの2つを選択してもらう必要がある。
  そこで、2つの引数を取るorder2というメソッドを追加する。order2のコードは次のようになる。

   void order2(int n1, int n2) {
    if(n1 < 0 || n1 > 2) ・・・・n1は主料理の選択
     select = 0;
    else
     select = n1;

    if(n2 < 0 || n2 > 2)
     select2 = 0;
    else
     select2 = n2;
   }

   また、親クラスのeatは主料理しか表示しないので、スープも表示するようにeat2を追加する。

   void eat2( ){
    System.out.println("料理=" + dishes[select]);
    System.out.println("スープ=" + soup[select2]);
   }

   以上で新しいTakeの宣言は終わりである。
  クラスTakeは、親クラスUmeのメンバーの他に2つのフィールドと2つのメソッドを追加して持つことになる。
  次に新しいTakeの全体とその使い方を示すサンプルを示す。

   サンプル:Soup.java

   //
   //Soup.java---メンバーを追加する
   //
   class Ume { ・・・・・親クラス
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    void order(int n) {
     if (n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( ) {
     System.out.println("料理=" + dishes[select]);
    }
   }

   class Take extends Ume { ・・・・子クラス
    int select2 = 0; //スープの種類

    String soup[ ] {
     "コンソメ",
     "ポタージュ",
     "カボチャスープ",
    };

    //n1は料理の選択(0-2)、n2はスープの選択(0-2)
    void order2(int n1,int n2) {
     if (n1 < 0 || n1 >2)
      select = 0;
     else
      select =n1;

     if(n2 < 0 || n2 > 2)
      select2 = 0;
     else
      select2 = n2;
    }

    void eat2( ) {
     System.out.println("料理=" + dishes[select]);
     System.out.println("スープ=" + soup[select2]);
    }
   }

   public class soup {
    public static void main(String[ ] args) {
     take t = new Take( );
     t.order(0, 2);
     t.eat( );
    }
   }

   実行例

   $ java Soup
   料理=ハンバーグ
   スープ=カボチャスープ
   $ _

   継承について次の段階に進む前にメソッドの多重定義について学んでおく。
  メソッドの多重定義というのは、要するに同じ名前のメソッドを定義する事である。
  同じクラスに同じ名前のメソッドを定義する事もできるし、子クラスで親クラスのメソッドと同じ名前のメソッドを定義する事も出来る。

同じクラスにおけるメソッドの多重定義

   「コンストラクタの多重定義」のところで、複数のコンストラクタを定義できる事を説明した。
  コンストラクタに限らず、一般的なメソッドについても多重定義する事ができる。
  すなわち、同じクラスの中に複数の同じ名前のメソッドを定義する事が出来る。これをメソッドの多重定義、又はメソッドのオーバ^−ロードという。
   多重定義されたメソッドは、コンストラクタの時と同様に引数によって区別される。
  したがって、メソッドを多重定義するときは、それらのメソッドが引数によって区別出来なければならない。

   ・ 引数の数を変える
   ・ 引数の数が同じなら、型を変える

   例えば次のクラスAには、funcという名前のメソッドが2つ定義されている。

   class A {
    void func(int n){ ・・・・これと
      …
    }

    void func(int n1, int n2) { ・・・・これ
      …
    }
   }

   前者のfuncは引数を1つ取る。また、後者のfuncは引数を2つ取るので区別が可能である。例えば、

   a.func(1000); ・・・・引数は1つ

   のようにしてfuncを呼び出せば、前者のfuncが実行される、また

   a.func(111, 222); ・・・・引数は2つ

   のようにしてfuncを呼び出せば、後者のfuncが実行される。
   以上をテストする為のサンプルを示す。

   サンプル:Over.java

   //
   //Over.java---メソッドのオーバーロード
   //
   class A {
    void func(int n) {
     System.out.println("引数は1つ:" + n);
    }

    void func(int n1, int n2) {
     System.out.println("引数は2つ:" + n1 + "," + n2
    }
   }

   public class Over {
    public static void main(String[ ] args) [
     A a = new A( );

     a.func(1000);
     a.func(111,222);
    }
   }

   実行例

   $ java Over
   引数は1つ:1000
   引数は2つ:111,222
   $ _

子クラスによるメソッドの多重定義

   メソッドの多重定義は、同じクラス内のみならず、継承した子クラスについても可能である。
  すなわち、親クラスにあるメソッドと同じ名前のメソッドを子クラスでも定義する事が出来る。前節で取り上げたUmeとTakeを例に説明する。
   UmeとTakeでは、似たようなメソッドorderとorder2が定義されている。メソッドorderは、クラスUmeの料理を注文する。
  クラスUmeでは、料理を1つしか選択できないので、orderの引数は1つであった。

   void order(int n)

   一方、クラスTakeではスープの選択が追加されているので、order2の引数は2つであった。

   void order2(int n1, int n2)

   メソッドは、多重定義出来るので、この2つの名前を変える必要は無い。つまり、order2はorderのままでよかったのである。

   void order2(int n1, int n2)
     ↓
   void order(int n1, int n2) ・・・・orderのままでよい

   Takeは、親クラスのorderと子クラスで多重定義したものの2つのorderを持つことになる。
  しかし、両者は引数が異なるので区別する事が可能である。

   t.order(1); ・・・・・・親クラスUmeのorderを実行する
   t.order(0,2) ・・・・・子クラスTakeのorderを実行する

   以上をテストする為のサンプル

   サンプル:Soup_2.java

   //
   //Soup_2.java---メソッドの多重定義
   //
   class Ume {
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    void order(int n) {
     if ( n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( ) {
     System.out.println("料理=" + dishes[select]);
    }
   }

   class Take extends Ume{
    int select2 = 0;

    String soup[ ] = {
     "コンソメ",
     "ポタージュ",
     "カボチャスープ",
    };

    void order(int n1, int n2) { ・・・・・親クラスと同じ名前
     if (n1 < 0 || n1 > 2)
      select = 0;
     else
      select = n1;

     if (n2 < 0 || n2 > 2)
      select = 0;
     else
      select = n2;
    }

    void eat2( ) {
     System.out.println("料理=" + dishes[select]);
     System.out.println("スープ=" + soup[select2]);
    }
   }

   public class Soup_2 {
    public static void main(String[ ] args) {
     Take t = new Take( );

     t.order(1);
     t.eat( );

     t.order(0,2);
     t.eat2( );
    }
   }

   実行例

   $ java Soup_2
   料理=ステーキ ・・・・・・t.order(1)のよる選択
   スープ=コンソメ
   料理=ハンバーグ ・・・・t.order(0,2)による選択
   スープ=カボチャスープ
   $ _

   なお、メソッドeatについては多重定義していない。これはeatもeat2も引数を取らない為に、引数で区別がつかないからである。

   void eat( ); ・・・・・Umeクラスのeat
   void eat2( ); ・・・・Takeクラスのeat

   実は、eat2をeatにする事も可能である。これについては、「多態性」のところで説明する。

super

   前項で取り上げた2つのorderを見ると、同じ処理内容のコードが2箇所に渡って書かれているのがわかる。


    void order(int n) {
     if ( n < 0 || n > 2) ・・・・・(A)
      select = 0;
     else
      select = n;
    }

    void order(int n1, int n2) {
     if (n1 < 0 || n1 > 2) ・・・・・(B)
      select = 0;
     else
      select = n1;

   この(A)も(B)も主料理をselectに設定するというもので、内容は全く同じものである。
  この場合、できれば(B)のところで親クラスのorderを呼び出して、そこで処理させる方がコードを簡略できる。
   このように親クラスのメソッドを子クラスで多重定義すると、一部は親クラスと同じコードになる事がある。
   superを使えば、親クラスのメンバーを参照する事が出来る。

   super.フィールド ・・・親クラスのフィールドを参照する
   super.メソッド( ) ・・・親クラスのメソッドを呼び出す

   superを置く位置は、親クラスを参照したい場所ならどこでも使用可能である。子クラスの任意の位置に置く事が出来る。
   Takeクラスのorderをsuperを使って書き換えたものを次に示す。

   void order(int n1, int n2) {
    super.order(n1); ・・・・・親クラスのorderを呼び出す

    if (n2 < 0 || n2 > 2)
     select2=0
    else
     select = n2;
   }

   次に新版のorderを使ってSoup_2.javaを書き換えたものを示す。

   サンプル:Super.java

   //
   //Super.java---super
   //
   class Ume {
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    void order(int n) {
     if ( n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( ) {
     System.out.println("料理=" + dishes[select]);
    }
   }

   class Take extends Ume{
    int select2 = 0;

    String soup[ ] = {
     "コンソメ",
     "ポタージュ",
     "カボチャスープ",
    };

    void order(int n1, int n2) { ・・・新版のorder
     super.order(n1);

     if (n2 < 0 || n2 > 2)
      select = 0;
     else
      select = n2;
    }

    void eat2( ) {
     System.out.println("料理=" + dishes[select]);
     System.out.println("スープ=" + soup[select2]);
    }
   }

   public class Super {
    public static void main(String[ ] args) {
     Take t = new Take( );

     t.order(1);
     t.eat( );

     t.order(0,2);
     t.eat2( );
    }
   }

   実行例は、前項のものと同じになる。すなわち、Takeのorderが正しく動いている事が確認される。

   $ java Soup_2
   料理=ステーキ
   スープ=コンソメ
   料理=ハンバーグ ・・・・Takeのorderが正しく機能している
   スープ=カボチャスープ
   $ _

明示的な呼び出し

   コンストラクタの用途の1つにフィールドの初期化がある。例としてUmeとTakeのメソッドorderをコンストラクタとして実装する。
  すなわちorderを廃止し、代わりにコンストラクタで注文を受け付ける。これにより、インスタンスの生成時に料理の注文を指定できる。
   まず、次がクラスUmeのコンストラクタである。コンストラクタの内容は、旧orderと同じである。

   class Ume {
    Ume(int n) { ・・・・・コンストラクタ
     if(n <0 || n > 2)
      select = 0;
     else
      select = n;
    }
      …
   }

   そして、Umeを継承したクラスTakeのコンストラクタである。

   class Take extends Ume {
    Take(int n1, int n2) {
     super.order(n1); ・・・・・ここが問題

     if(n2 < 0 || n2 > 2)
      select2 = 0;
     else
      select2 = n2;
    }
   }

   問題は、この中のsuper.order(n1);の部分である。ここでは、親クラスのメソッドorderを呼び出している。しかし、もはやorderは存在しない。
  代わりに親クラスのコンストラクタを呼び出さなくてはならない。では、その記述をどうしたらよいだろう。
   親クラスのコンストラクタ名はUmeなので(Umeはクラス名でもある)orderの部分をUmeと書けばよいのだろうかというと、そうではない。
   正解は「何もつけない」である。すなわち次のように書く。

   super(n1);

   このように子クラスで親クラスのコンストラクタを呼び出すときは、メソッド名を指定せずにsuperを使う。
   一般的なメソッドを呼び出すときは、superを置く位置は任意であった。
  しかし、コンストラクタの呼び出しだけは、superをコンストラクタの先頭に置かなければならない。

   class Take extends Ume {
    Take(int n1, int n2) {
     super(n1); ・・・・・これ以外の位置に置いてはいけない

     if(n2 < 0 || n2 > 2)
      select = 0;
     else
      select = n2;
    }
   }

   次にUmeとTakeにコンストラクタを導入して書き換えたものを示す。

   サンプル:Cons.java

   //
   //Cons.java---親クラスのコンストラクタの呼び出し
   //
   class Ume {
    int select = 0;

    String dishes[ ] = {
     "ハンバーグ",
     "ステーキ",
     "魚",
    };

    Ume(int n) {
     if ( n < 0 || n > 2)
      select = 0;
     else
      select = n;
    }

    void eat( ) {
     System.out.println("料理=" + dishes[select]);
    }
   }

   class Take extends Ume{
    int select2 = 0;

    String soup[ ] = {
     "コンソメ",
     "ポタージュ",
     "カボチャスープ",
    };

    void order(int n1, int n2) { ・・・新版のorder
     super(n1);

     if (n2 < 0 || n2 > 2)
      select = 0;
     else
      select = n2;
    }

    void eat2( ) {
     super.eat( );
     System.out.println("スープ=" + soup[select2]);
    }
   }

   public class Cons {
    public static void main(String[ ] args) {
     Take t = new Take(1,2 ); ・・・・・料理の注文をコンストラクタでする
     t.eat2( );
    }
   }

   実行例

   $ java Cons
   料理=ステーキ
   スープ=カボチャスープ
   $ _

明示的に呼び出さないとき

   子クラスのコンストラクタで、明示的に親クラスのコンストラクタを呼び出す(superを使う)場合は特に問題は無い。
  逆に子クラスのコンストラクタでsuperを使わない場合は注意が必要である。
  というのは、自動的にコンストラクタの先頭に引数無しのsuper( )が挿入されるからである。
   例えば、次の子クラスchildのコンストラクタにはsuperが使われていない。

   class Child extends Parent {
    Child( ) { ・・・・・子クラスのコンストラクタ
     System.out.println("・・・・・"); ・・・・・先頭にsuperがない
    }
   }

   この場合は、自動的にsuper( )が挿入される。つまり、Childのコンストラクタは次と同じになる。

   class Child extends Parent {
    Child( ) {
     super( ); ・・・・・自動的に挿入される
     System.out.println("・・・・・");
    }
   }

   このように子クラスのコンストラクタは、明示(superを使う)/非明示(superを使わない)の区別なく親クラスのコンストラクタが呼び出される。

   以上をテストする為のサンプル

   サンプル:Cons_2.java

   //
   //Cons_2.java---親クラスのコンストラクタを呼び出さないとき
   //
   class Parent {
    Parent( ) [
     System.out.println("Parentのコンストラクタ");
    }
   }

   class Child extends Parent {
    Child( ) {
     System.out.println("Childのコンストラクタ");
    }
   }

   public class Cons_2 {
    public static void main(String[ ] args) {
     Child c = new Child( );
    }
   }

   このサンプルでは、mainの中で子クラスのインスタンスを生成している。

   public static void main(String[ ] args) {
    Child c = new Child( );
   }

   この結果、子クラスのコンストラクタが実行される。そのコードは次のようになっている。

   Chile( ) {
    System.out.println("Childのコンストラクタ");
   }

   これを見る限り、親クラスのコンストラクタは呼び出していない。しかし、先の説明によると、自動的に呼び出されるはずである。
  その親クラスのコンストラクタは次のようになっている。

   Parent( ) {
    System.out.println("Parentのコンストラクタ");
   }

   果たして、このコードが実行されるのか、以下が、その実行例

   $ java Cons_2
   Parentのコンストラクタ ・・・・親クラスのコンストラクタが実行されている
   Childのコンストラクタ ・・・・・子クラスのコンストラクタの実行はその後になる
   $ _

メンバーに対するアクセス制御(3)

   前章までに、2つのアクセス修飾子、およびアクセス修飾子のない場合について説明した。

   ・ private ― この修飾子で修飾されたメンバーは他のクラスから参照する事はできない。(そのメンバーを外部に非公開にする)
   ・ public  ― この修飾子で修飾されたメンバーは他のクラスから参照する事が出来る。(そのメンバーを外部に公開する)
   ・ なし  ― アクセス修飾子で修飾されていないメンバーは同じパッケージにあるクラスからのみ参照できる。

   本節では、最後のアクセス修飾子としてprotectedを取り上げる。

親クラスのprivateメンバー

   privateで修飾されたメンバーは他のクラスから参照する事ができない。
  例えば次のフィールドメソッドKeyやメソッドsetKeyがprivateメンバーである。

   class Parent {
    private int Key; ・・・・・privateフィールド

    private void setKey(int n) { ・・・・・privateメソッド
     Key = n;
    }
   }

   これらのメンバーは、同じクラスのメソッドからのみ参照する事が出来る。
   さて、このクラスのParentを継承してChildという子クラスを作ったとする。子クラスは親クラスの全てのメンバーを受け継ぐ。
  にもかかわらず、親クラスのprivateフィールドを参照できない。つまり、次のようなことは出来ない。

   class Child extends Parent {
    void showKey( ) {
     System.out.println("Key=" + Key); ←親クラスのprivateフィールドを参照
    }
   ]

   もし、このようなコードを書くと、コンパイル時に次のようなエラーとなる。

   BaseField.java:14: シンボルを解釈処理できません。
   シンボル: 変数 Key
   位置: Child の クラス
     System.out.println("Key=" + Key);
                       ^
   エラー1個
   $ _

   もし子クラスで親クラスのprivateフィールドを参照したいなら、
  そのフィールドの値を返すpublicメソッドを作って、代わりにそのメソッドを呼び出すようにする。
  たとえば、Keyの値を返すgetKeyというメソッドを作り、そのメソッドをpublicにする。

   class Parent {
    private int Key; ・・・・・privateフィールド

    public int getKey( ) { ・・・・・これはpublicメソッド
     return Key; ・・・・・privateフィールドを返す
    }
   }

   そして、先のコードを次のように書き換える。

   class Child extendsParent {
    void showKey( ) {
     System.out.println("Key=" + getKey( )); ←これはpublicメソッドなので参照可能

   以上をテストする為のサンプル

   サンプル:BaseField.java

   //
   //BaseField.java---親クラスのprivateフィールド
   //
   class Parent {
    private int Key = 3859;

    public int getyKey( ) {
     retuen Key;
    }
   }

   class Child extends Parent {
    void showKey( ) {
     System.out.println("Key=" + getKey( ));
    ]
   ]

   public class BaseField {
    public static void main(String[ ] args) {
     Chile c = new Child8 );
     c.showKey( );
    }
   }

   実行例

   $ java BaseField
   Key = 3859
   $ _

protectedメンバー

   前項では、親クラスのprivateフィールドを参照する代わりに、そのフィールドの値を返すpublicメソッドを作り、そのメソッドを呼び出すようにした。
  しかし、この方法ではprivateフィールドの方は何とかなっても、privataeメソッドを参照する事は出来ない。
   あるメンバーを外部には公開したくないが、子クラスからは参照出来るようにしたいことがある。
  そのような時は、第3のアクセス修飾子を導入する事によって可能になる。すなわちprivateの代わりにprotectedを使うのである。

   class Parent {
    protected int Key; ・・・・・protectedフィールド

    protected void setKey(int n) { ・・・・・protectedメソッド
     Key = n;
    }
   }

   protectedで修飾されたメンバーは、privateメンバーと同様に外部からの参照を拒否する事が出来る。
  しかも、privateメンバーとは違って、子クラスのメソッドから参照する事が出来る。
   例えば、今のように修正した親クラスParentから次のような子クラスを作ったとする。

   class Child extends Parent { ・・・・・parentを継承
    public void showKey( ) {
     setKey(987654321); ・・・・・親クラスのprotectedメソッド
     System.out.println("Key=" + Key); ←親クラスのprotectedフィールド
    }
   ]

   子クラスのメソッドshoeKeyの中でsetKeyやKeyを参照している。これらは親クラスのprotectedメンバーである。
  もしこれらがprivateメンバーならエラーになるところである。
  しかし、protectedメンバーなら参照可能である。わざわざpublicメソッドを作る必要は無い。

   以上をテストする為のサンプル

   サンプル:Protected.java

   //
   //Protected.java---protectedメンバー
   //
   class Parent {
    protected int Key = 3859;

    protected void setKey(int n) [
     Key = n;
    }
   }

   class Child extends Parent {
    public void showKey( ) {
     setKey(987654321);
     System.out.println("Key=" + Key);
    }
   }

   public class Protected {
    public static void main(String[ ] args) [
     Child c = new Child( );
     c.showKey( );
    }
   ]

   実行例

   $ java Protected
   Key=987654321
   $ _

   これでメンバーに対するアクセス修飾子が出揃ったことになる。まとめてみると、以下のようになる。

アクセス修飾子 意 味
 private   この修飾しで修飾されたメンバーは他のクラスから参照する事は出来ない。
 子クラスのメソッドからも参照されない。(メンバーを外部と下位ともに非公開にする)
 protected   この修飾子で修飾されたメンバーは他のクラスから参照する事は出来ない。
 子クラスのメソッドからは参照できる。(メンバーを外部だけ非公開にする)
 public   この修飾子で修飾されたメンバーは他の暮らすから参照する事が出来る。
 (そのメンバーを外部に公開する)
 なし   アクセス修飾子で修飾されていないメンバーは、
 同じパッケージにあるクラスからのみ参照できる。

次へ