連載
» 2003年09月17日 00時00分 UPDATE

いまから始めるJava(9):クラスのメンバに利用制限を付与するアクセス制御

[平井玄,@IT]

 ここまでの連載で、クラスの基礎的な理解は終えていただけていると思います。今回は、クラスのメンバであるメソッドや変数について、もう少し掘り下げていくことにします。

 本題に入る前に、少し復習をします。Java的なプログラムを記述するには、ユーザーが扱うのはどんなデータなのかを念頭に置いて、例えばHTML文書を扱うならHTMLDocumentというクラスを設計する、といった発想が必要であることは理解いただけていると思います。ユーザーがWebブラウザで扱うのはHTML文書ですので、WebブラウザをJavaで作るとしたら、まず設計しなくてはならないのはHTML文書を扱う「HTMLDocument」などという名前のクラスになるはずです。

クラスをWebブラウザで理解する

 HTMLDocumentというクラスは制御文を使ったアルゴリズムの実例として「第7回 制御文を使いこなす」で取り扱いました。といっても、メンバ変数としてString型のsourceにHTML文書のソースを格納することと、ソースからタグを取り除いたテキスト部分を表示するshowPlainText()というメソッドだけの、とても簡単なものです。

 では、いまこの文書を読んでいるWebブラウザがInternet Explorerの読者は、[ファイル]メニューから[名前を付けて保存]を選び、[Webページの保存]ダイアログにある[ファイルの種類]ドロップダウンから[テキストファイル]を選んで保存してみてください。showPlainText()メソッドとまったく同じ機能ではありませんが、HTML文書からテキストを取り出すというよく似た機能がInternet Explorerにもあることが分かります。Internet Explorerが1つのインスタンスだとすれば、メニューに並んでいる項目がメソッドに相当するわけです。Internet Explorerのメニューにはほかにもさまざまな項目が並んでいますが、[ファイル]メニューの[印刷]や[プロパティ]もInternet Explorerによって生成されたHTMLDocument型のインスタンスのメソッドなのだ、と理解できます。

 では、HTML文書のソースを格納しているメンバ変数sourceはWebブラウザのどこにあるのでしょうか? [表示]メニューから[ソース]を選ぶと表示中のHTML文書のソースが表示されますが、sourceそのものを操作できるわけではありません。

 実は、HTML文書のソースはユーザーからは見えないところにあるので、ユーザーは直接操作できないのです。[表示]メニューの[ソース]は、showSource()のようなメソッドを呼び出すためのメニューで、変数そのものではありません。

 そもそもメンバ変数sourceはHTML文書のソースを格納するためのものです。もしユーザーが操作できてしまうと、HTML文書以外のデータを入れられたりして、sourceにHTML文書が格納されていることを前提にしているshowPlainText()のようなメソッドの動作がおかしくなってしまいます。sourceの内容を変えられるのは、URL欄に入力されたWebサイトのHTML文書を読み出す機能のように、ユーザーの指示を実際に処理するInternet Explorer内部のメソッドだけ、ということにしておくわけです。

アクセス制御

 クラスは一種の道具ですので、String型のメソッドであるlength()やこの連載で作成したHTMLDocument型のメソッドであるshowPlainText()が、実際にどのような処理をしているのか、プログラマは知っている必要はありません。プログラマは単にString型には文字列の長さを調べるlength()、HTMLDocument型にはテキストを表示するshowPlainText()というメソッドがある、とだけ知っていればよいのです。

 しかし、読者の皆さんはHTMLDocument型にsourceというメンバ変数があって、HTML文書を格納していることを知っています。例えば以下のようにすれば、でたらめのデータを格納できてしまいます。

HTMLDocument doc = new HTMLDocument();
doc.source = "abcdedg <HTML> >< </BODY>";

 sourceというメンバ変数があることを知っていても、勝手にsourceを変更できないような仕組みが必要です。そこでJavaには、「アクセス制御」という考え方があります。クラスのメンバである変数やメソッドをどういう場合に利用できるのかをクラスの定義として記述できるのです。

 アクセス制御にはpublic、private、protectedというキーワードを使います。HTMLDocumentのクラス定義をアクセス制御を使って書き直すと以下のようになります。

class HTMLDocument {
private String source;
public void showPlainText() {
// 省略
}
}

 HTMLDocumentというクラスにメンバ変数sourceとメソッドshowPlainTextが定義されています。しかし、変数sourceはprivateに指定されていますので、プログラマは勝手にsourceを操作できなくなります。以下のプログラムをコンパイルしてみましょう。

class HTMLDocument {
    private String source;
    public void showPlainText() {
        // 省略
    }
}


public class AccessViolation {
    public static void main( String args[] ) {
        HTMLDocument doc = new HTMLDocument();
        

doc.source = "<html>TEXT</html>";
        doc.showPlainText();
    }
}

   実行結果
C:\DOCUME~1\MYDOCU~1\MYJAVA~1>javac AccessViolation.java
AccessViolation.java:12: source は HTMLDocument で private アクセスされます。
    doc.source = "<html>TEXT</html>";
       ^

エラー 1 個

C:\DOCUME~1\ MYDOCU~1\MYJAVA~1>

 privateに指定された変数sourceをクラスの外側から使おうとしたので、コンパイラがエラーを検出しました。アクセス制御を使うことで、sourceというメンバ変数があることを知っていても、勝手に操作できなくなったのです。

 ただし、このままではHTMLDocumentにHTML文書のソースを読み込ませる方法がなくなってしまいます。そこで、HTML文書のソースを読み込んで変数sourceに格納する専用のメソッドとしてsetSource()を作ることにします。

class HTMLDocument {
    private String source;
    public void setSource( String html ) {
        if ( html.indexOf("<html>") == 0 )
        source = html;
}
    public String getSource() {
        return source;
    }
    public void showPlainText() {
        // 省略
    }
}


public class HTMLDocumentWithAccesser {
    public static void main( String args[] ) {
        HTMLDocument doc = new HTMLDocument();

        doc.setSource("<html>TEXT</html>");
        doc.showPlainText();
    }
}

 今度はエラーを検出することなくコンパイルできたはずです。setSource()はメンバ変数sourceに値を代入するためのメソッドですが、単に代入するだけではなく、引数htmlの内容を検査しています。String型の引数htmlが「<html>」で始まるかどうかを調べるという非常に簡単な検査ですが、変数sourceにでたらめな値を入力させない方法は分かったと思います。

public void setSource( String html ) {
    if ( html.indexOf("<html>") == 0 )
    source = html;
}

 改良したプログラムではもう1つgetSource()というメソッドも追加しました。変数sourceをprivateに指定したため、クラスの外側からはsourceを読み出すこともできなくなってしまいました。これでは不便ですので、変数sourceを読み出す専用のメソッドとしてgetSource()を用意したのです。

public String getSource() {
    return source;
}

 setSource()やgetSource()のように、privateに指定されたメンバ変数に外部からアクセスするためのメソッドを「アクセサ」といいます。アクセサを使うと、インスタンスが保持する重要な変数の値を勝手に書き換えられないように保護したり、値が変更されるときに変更後の値が適切かどうかをチェックできるようになります。

パブリックとプライベートの意味

 日本人はついついpublicとprivateを「公」と「私」に置き換えてしまいます。日本語の「公」と「私」は「公私混同」などというように性質を表す概念で、「公」は個人の所属する組織(語源的には朝廷のこと)のこと、「私」は「私事」などというように「公」とは切り離されていることやものを表します。これでは、Javaのアクセス制御に使われるpublicとprivateの意味をなかなかつかめません。

 実は、英語のpublicとprivateは空間を表す概念です。2つの言葉はちょうど卵の白身と黄身のような関係で、外側がpublic、内側がprivateになります。ちょっとおしゃれなカフェにいくと、従業員の控え室や物置のドアに「private」と書いてあることがあります。外国を旅行された方なら、列車の車掌室のドアに「private」と書いてあることに気付いたかもしれません。カフェにしても列車にしても、誰でも利用できる外側の空間がpublic、関係者しか利用できない内側の空間がprivateになります。

 Javaのpublicとprivateも同じことです。クラスのインスタンスを生成したプログラムが利用できる変数やメソッドはpublicに、クラスのメソッドが内部で作業用に使う変数やメソッドはprivateに指定します。

 もう一度Webブラウザの話に戻りましょう。Webブラウザのメニュー項目に対応するようなメソッドは、ユーザーが操作してもよいpublicなメソッドに指定します。しかし、実際にテキストを取り出してファイルに書き出すような、publicなメソッドから読み出される作業用のメソッドはprivateに指定します。

 ちなみに、これまで例として示してきたソースプログラムでは、クラスのメンバ変数やメソッドにpublicやprivateを指定していませんでした。この場合、そのメンバ変数やメソッドは同じファイル内からは自由にアクセスできる特殊なアクセス制御ということができます。

アクセス制御と継承

 アクセス制御はクラスを継承したときにも引き継がれます。以下のプログラムをコンパイルするとエラーになります。

class HTMLDocument {
    private String source;
    public void setSource( String html ) {
        if ( html.indexOf("<html>") == 0 )
        source = html;
    }
    public String getSource() {
        return source;
    }
    public void showPlainText() {
        // 省略
    }
}

class HTMLDocument2 extends HTMLDocument
{
    public void showSource() {
        System.out.println(source);
    }
}

public class AccessViolation2 {
    public static void main( String args[] ) {
        HTMLDocument2 doc = new HTMLDocument2();

        doc.setSource("<html>TEXT</html>");
        doc.showSource();
    }
}

   実行結果
C:\DOCUME~1\MYDOCU~1\MYJAVA~1>javac AccessViolation2.java
AccessViolation2.java:18: source は HTMLDocument で private アクセスされます。
    System.out.println(source);
       ^

エラー 1 個


C:\DOCUME~1\MYDOCU~1\MYJAVA~1>

 エラーが検出されるのは以下の部分です。

class HTMLDocument2 extends HTMLDocument
{
    public void showSource() {
        System.out.println(source);
    }
}

 クラスHTMLDocument2はHTMLDocumentのサブクラスです。しかし、HTMLDocumentのメンバ変数であるsourceはprivateに指定されているので、サブクラスで定義されたメソッドshowSource()からはアクセスできないのです。

 この例の変数sourceのように、クラス外部からのアクセスは制限したいが、サブクラスからのアクセスは許可する必要がある場合があります。このようなとき、Javaではprotectedというキーワードを使います。protectedに指定された変数やメソッドは、クラス外部からはprivate同様にアクセスできませんが、クラス内部や、サブクラスからはpublicのようにアクセスできます。従って、この例の場合は以下のようにプログラムを書き換えるとエラーにならなくなります。

class HTMLDocument {
    protected String source;
    …
}

 さて、この連載ではこれまでJavaの必要最低限な機能だけを使って基本的な言語仕様を説明してきました。ですが、そろそろファイルの入出力など、より高度な機能を使わないと説明が単調になってしまいます。そこで次回はJavaでさまざまな機能を利用するための要となるパッケージについて説明します。


Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。