プログラミング応用a 第9回 『モジュール化その2 (パッケージ化とインポート、パッケージレベルでのアクセス制御)』  

【パッケージ】

●クラスは,小さい『モジュール』であった。関連するクラスをまとめて,『パッケージ (package)』というより大きいモジュールにまとめることができる。
 ※パッケージは,ファイルシステム上ではフォルダとして表現されている(下図)。
 ※さらに,複数のパッケージをまとめて,より上位のパッケージを作ることができる。下位のパッケージをサブパッケージと呼ぶ。
  例)下図は, Java に元から用意されているパッケージ(標準パッケージ)の階層の一部である。
    ・ java という最上位のパッケージのサブパッケージとして awt, io, text, lang, math, net などがあることが分かる。
    ・ java のサブパッケージ awt には,さらにサブパッケージとして color, image, font などがあることが分かる。

■確認問題:上図に示した標準パッケージの一部の font パッケージの完全限定名は java.awt.font である。では, image パッケージの完全限定名は?

 


【クラスをパッケージへ編入する方法(package宣言)】

●あるクラスを特定のパッケージに編入したい場合は,ソースファイルの先頭で package 宣言を行う(下図)。


【別のパッケージ内のクラスを使う(import宣言)】

 あるクラスから別のクラスを利用する場合,同一パッケージ内のクラスは特別に何もしなくても使用することが出来る。
しかし,別のパッケージ内のクラスを使うには,ソースファイルの先頭で import 宣言を行わなくてはならない(下図)。





【パッケージとアクセス指定】

●あるパッケージのクラスを他のパッケージからも利用可能にするためには, クラス定義で public と指定してクラスを定義しなくてはならない。
(下図をクリックすると補助情報が表示されます)


表にまとめると次の様になる。

  指定無しクラス publicクラス
同一パッケージ内のクラスから
別のパッケージ内のクラスから ×

※つまり,publicクラスへはどこからでもアクセスできる




【メンバのアクセス指定ふたたび】

● メンバのアクセス特性を指定するとき, private 指定の他に,「指定無し」があったが,新たに「public指定」を紹介する。

  privateメンバ 指定無し(デフォルトアクセス)メンバ publicメンバ
同一クラス内から
同一パッケージ内のクラスから ×
別のパッケージ内のクラスから × ×

※その他にprotectedというメンバアクセス指定があるがここでは省略する
※つまり,publicメンバはどこからでもアクセスできる

●図で見るprivateメンバ/デフォルトアクセスメンバ/publicメンバ

※つまり, public クラス(厳密には,publicクラスのpublicメンバ)はパッケージのインタフェイス部を構成する。
※それ以外(publicでないクラス,publicクラスのpublic以外のメンバ )は,パッケージの実装部を構成する。


【パッケージとディレクトリ構成】

 パッケージに属するクラスは,パッケージ構成を反映したディレクトリの中になくてはならない。
たとえば,Meibo.javaのMeiboクラスとPersonクラスは,tuisjava.comというドメインを所有して
る会社が開発した人事評価用ソフトの一部だとしよう。この2つのクラスを,com.tuisjava.jinjiとい
うパッケージにまとめる場合,「com」ディレクトリの中の「tuisjava」ディレクトリの中の「jinji」
というディレクトリの中に,Meibo.classとPerson.classを置かなくてはならない(下図Fig.10参照)。
 このとき,comディレクトリを置くディレクトリ(名前は自由につけても良いが,Fig.10の例では
myclassesという名前になっている)
の場所を,コンパイラコマンドjavacや,実行コマンドjavaに,
知らせてやる必要がある。その方法には2種類あるが,それについては後ほど解説する。


では,実際に下記の演習でMeibo.javaをパッケージ化してみよう。

●演習
 手順0)先ほど作成した
      Meibo.java,Meibo.class ,Person.class,MeiboTest01.class
     を削除せよ。これらは,パッケージ化する前のものなので,もう必要がない。
     (というよりも,有ると以降の演習の邪魔になる)

 手順1)下図(Fig.10a)のように,「マイドキュメント」(Z:¥MyDocuments)ディレクトリの下に,
     myclassesディレクトリ以下,jinjiディレクトリまでを作成せよ(綴りを間違えないように
     気をつけること)


 手順2)Person.java(package宣言無しバージョン)Meibo.java (package宣言無しバージョン) を,
     上図のjinjiディレクトリに保存し,下図のように
      ・それぞれ1行目にpackage宣言を書き加え
      
Personクラス自体とのPersonのコンストラクタをpublicに指定
      
Meiboのコンストラクタと,put()メソッド,printAll()メソッドをpublicに指定して
     
保存し直せ。






今,状況は次のようになっている。



 手順3)先ほどのMeiboTest01.java (import宣言無しバージョン) に,下図(1)のように1行目にimport宣言を付けて
     保存する。保存場所はどこでもいいが, Z:¥ooprog など,いつもjavaのプログラムを作成しているディレク
     トリでよだろう。


      次に,このMeiboTest01.javaをコンパイルするわけだが,このプログラムはimport宣言にあるように,
      com.tuisjava.jinji パッケージのMeiboクラスを利用する。
      前述したように,パッケージ化したクラスを利用するには,コンパイル時とプログラム実行時に,
       ・パッケージの起点となるディレクトリ(Fig.10の例ではmyclassesディレクトリ)の場所
     を,javacコマンドや,javaコマンドに教えておく必要がある。(ちなみに,コンパイルやプログラムの実行時
     に必要になるクラスの場所を示すファイルパス(file path name)のことを,クラスパス(class path)と呼ぶ。ここ
     で指定するパッケージの起点となるディレクトリの場所もクラスパスの一種である)
      パッケージの起点となるディレクトリの場所をjavac/javaコマンドに教える方法には,次の2つがある。
       i) コンパイル時やプログラム実行時に,そのつどパッケージの起点となるディレクトリの場所を指定する
       ii) 環境変数CLASSPATHに,パッケージの起点となるディレクトリの場所を追加する
     ここでは,i)の方法を紹介する。(ii)の方法についてはこちらを参照せよ)

     コンパイルは,次のように行う。
       (1) コマンドプロンプトを表示して,MeiboTest01.java を保存したディレクトリにcdコマンドで移動。
         例:もし,MeiboTest01.java を Z:¥ooprog に保存しているなら,
            > cd Z:¥ooprog
           とする。
       (2) 次のように,パッケージの起点となるディレクトリの場所を指定してMeiboTest01.java をコンパイルする。
         > javac -cp "Z:\MyDocuments\myclasses" MeiboTest01.java
               ※-cpは,javacコマンドのクラスパスを指定するためのオプションである。javacコマンドのオプションについては
                 > javac -help
                とすれば概要が表示される。

     コンパイルがうまくいかない場合は,
       ・ファイル配置が,手順2のFig.10のようになっているか(ディレクトリ名の間違いなどにも注意)
       ・Meibo.javaに,正しくpackage宣言が書かれているか
       ・MeiboTest01.javaに,正しくimport宣言が書かれているか
     を確認せよ。

     コンパイルがうまくいくと,Meibo.classとPerson.classが jinji ディレクトリの中に出来ているはずであるので,
     確認せよ(下図)。


     ここで起こったことを順序立てて書くと次のようになる。
      1)javacコマンドがMeiboTest01.javaをコンパイルするとき,必要になるMeiboクラスが見つか
        らないので,-cpオプションで指定されたファイルパスによりパッケージ階層の起点であるディ
        レクトリ(この場合,Z:\MyDocuments\myclasses)を割り出す。
      2)次いで,パッケージ階層の起点ディレクトリ(myclasses)とimport宣言の内容から
          Z:\MyDocuments\myclasses\com\tuisjava\jinji
        ディレクトリにクラスファイルMeibo.classがあるのではないかと推測する。
      3)しかし,そのjinjiディレクトリ内には,コンパイル済みのMeibo.classはまだ無い。そこで
        javaコンパイラは,Meiboクラスを定義しているはずのソースファイルMeibo.javaを探す。そ
        うして,jinjiディレクトリ内にMeibo.javaを見つけたjavaコンパイラは,Meibo.javaを
        コンパイルしてjinjiディレクトリの中に,Meibo.classとPerson.classを生成したのである
        (上図の矢印)。Personクラスについても同様である。

   手順6)手順の5でコンパイルしたMeiboTest01.classを実行してみよう。コマンドプロンプトの現在位置
       (カレントディレクトリ)にMeiboTest01.classがあることを確認せよ。実行コマンドは以下のよう
       になる。クラスパスに,MeiboTest01.classの在処であるカレントディレクトリ(.)を追加している
       ことに注意。なお,カレントパスの区切り文字はWindowsではセミコロン(;),Unixではコロン(:)が
       使われる。
        > java -cp "Z:\MyDocuments\myclasses;." MeiboTest01
               ※-cpは,javaコマンドのクラスパスを指定するためのオプションである。javaコマンドのオプションについては
                 > java -help
                とすれば概要が表示される。


 


※Eclipseで,パッケージを扱う方法については,左欄の「4. Eclipseでパッケージを作成する方法」のリンク先を参照せよ。