非IT企業に勤める中年サラリーマンのIT日記

非IT企業でしかもITとは全く関係ない部署にいる中年エンジニア。唯一の趣味がプログラミングという”自称”プログラマー。

インターフェースの必要性についてJavaを使って解説(1)

      2017/04/04

ネットしてたら、Javaを勉強中の人がインターフェースって何のメリットがあるかわからないと投稿しているのを発見。僕もかつてはインターフェースって何のためにあるんだろうと悩んだ時期があったので、なつかしいなと思いました。

だって空っぽのメソッドを規定するだけのものだもんね。わかるわかる。

それに対して、お決まりの教科書的な回答があったんですが、確かに(理解した今の僕には)わかりやすい解説なのですが、インターフェースがさっぱりわかっていない人にはおそらく腹に落ちないと思います。かつての僕自身もそうだったし。

解説ってつい理論から攻めがちなのですが、理論から教えるって知識不十分の状態の人に対してだと、かえって混乱してしまうんですよね。なので、このブログでは逆に帰納的というか、実際に開発を想定してプログラムを組みながらインターフェースの必要性を説明したいと思います。

[ad#top-1]

あなたは開発者で、作ったコンポーネントを公開しようとしているとする

あなたはプログラマーで、1つのスタンドアロンのソフトではなく、ソフト開発に使うためにコンポーネント開発者だとします。

↓こんな感じのダイアログで、ユーザーにテキストボックスに文字を入れて、ボタンを押すと、ユーザ側の親ウィンドウに文字を返すダイアログを開発しているとします。このダイアログは不特定多数のプログラマーに使ってもらうことが目的です。

 

青い枠線のウィンドウがあなたが作るダイアログ、茶色の枠線のウィンドウはユーザー側の親ウィンドウになります。この時、ユーザー側の親ウィンドウのオブジェクト名は当然ながら不明です。

 

さて、どうやって文字を返すか?

「あなたの作るプログラム」のソースコードは以下の通りです。便宜上、クラス名をMyDialogとしました。※プログラムの都合上、インターフェースActionListenerは使います。

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

public class MyDialog extends JDialog implements ActionListener{
  protected Container pane;
  Window owner;
  JTextField textbox;
 
  MyDialog(Window owner){
    super(owner);
    this.owner = owner;
    setSize(200, 100);
    pane = getContentPane();
    pane.setLayout(new FlowLayout());

    textbox = new JTextField(10);
    pane.add(textbox);
    JButton btn = new JButton("Submit");
    pane.add(btn);
    btn.addActionListener(this);
  }

  public void actionPerformed(ActionEvent e){
    //ここにユーザー側に文字を投げるコードを書く
  }
}
 

 

ボタンを押したときに実行されるactionPerformedメソッド内に、ユーザーに値を投げる処理を書かなければなりません。さて、どうしましょう??

ちなみに、このプログラムをユーザーが使うことを想定したプログラムが以下です。クラス名はTestとしていますが、「不特定多数のユーザー」なのでいろいろな名前が付きます。

ボタンを押すとあなたの作ったダイアログが開くようになっています。(26行目以降のところ)

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

public class Test extends JFrame implements ActionListener{
  protected Container pane;

  public static void main(String[] args){
    Test f = new Test("Test");
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }

  Test(String t){
    super(t);
    setSize(400, 300);
    pane = getContentPane();
    pane.setLayout(new FlowLayout());

    JButton btn = new JButton("Submit");
    pane.add(btn);
    btn.addActionListener(this);
  }

  public void actionPerformed(ActionEvent e){
    MyDialog d = new MyDialog(this);
    d.setLocationRelativeTo(null);
    d.setVisible(true);
  }
}
 

 

よし!こちらからメソッド名を指定しよう!

値の投げ先に(つまりユーザー側のクラスに)、こちらから指定したメソッドを書いてもらうってことで解決するでしょうか??

相手に書いてもらうメソッドを、hoge(string str)としましょう。

「俺のMyDialogを使いたければ、そっちでhogeメソッドを書け!(じゃないと、コンパイルエラーになるよ)」って感じです。一般的にコールバック関数名を指定するのはアリです。

なので、ユーザー側のプログラムに以下のコードを書いてもらいます。

public void hoge(string str){
  //strが得られる
}
 

 

さて、これで解決、文字を返すコードを書こう・・・、ってなったところで、問題にぶち当たります。それは、相手のオブジェクトを指定しないとメソッドにアクセスできないってことです。

public void actionPerformed(ActionEvent e){
  ????.hoge(textbox.getText());
}
 

 

さて、相手のオブジェクトをゲットするにはどうするか? ユーザーがMyDialogを呼び出すとき、

MyDialog d = new MyDialog(this);

と、this(自分)を投げてくれているから、それを使えばいいってことにならないでしょうか。

こっちは、コンストラクタでMyDialog(Window owner)と受け取った後、this.owner = owner;としているから、こちらの投げる部分、actionPerformedメソッド内は以下のようになります。

public void actionPerformed(ActionEvent e){
  owner.hoge(textbox.getText());
}
 

 

結果

で、コンパイルしたところ、以下のようにコンパイルエラーが発生してしまいました。

エラー: シンボルを見つけられません
owner.hoge(textbox.getText());
^
シンボル: メソッド hoge(String)
場所: タイプWindowの変数 owner

エラー1個

 

要は、ownerという変数にhogeメソッドが見つかりません、というエラーメッセージです。ユーザー側でちゃんと定義したはずなのにダメでした。

それもそのはずで、ownerはWindowクラスのオブジェクトなので、Windowクラス(とその上位クラス)のメソッドしか使えないんです。hogeクラスはあくまでユーザーのクラス内で定義するメソッドなので、owner.hogeでは呼び出せないんです。

なので、以下のようにすればコンパイルできるわけです。

・・・
public class MyDialog extends JDialog implements ActionListener{
  protected Container pane;
  Window owner; ←ここをWindowじゃなくユーザーのクラス名にする
  JTextField textbox;
 
  MyDialog(Window owner){ ←ここをWindowじゃなくユーザーのクラス名にする
    super(owner);
    this.owner = owner;
    ・・・

 

Windowで宣言したownerを、親ウィンドウであるユーザー側のクラス名にすればOKです。

しかし、ここでも問題があります。

なぜなら、あなたはユーザー側のクラス名を知らないのです。不特定多数に使ってもらうために作っているので、ユーザーがどんなクラス名か知る由もありません。

「ユーザーにクラス名を指定する」とすれば解決できます。

例えば、ユーザーに「Testというクラス名にしてね。じゃないとコンパイルエラーになるよ」とすれば、あなたのプログラムを以下のようにすれば解決できます。

・・・
public class MyDialog extends JDialog implements ActionListener{
  protected Container pane;
  Test owner;        ←ここ
  JTextField textbox;
 
  MyDialog(Test owner){ ←ここ
    super(owner);
    this.owner = owner;
    ・・・

 

しかし、相手にクラス名まで指定して強いるというのはあり得ません。自分1人ですべてのプログラムを書くのなら問題ありませんが、不特定多数の誰かに対してはさすがにそれはできないのです。(もちろん、相手がわかっているプロジェクトチームの誰かにも、です。)

じゃあ、どうやって相手に値を送ることができるのか?

ちょっと長くなりそうなので、今回はいったんここで切って続きは次回にしたいと思います。

.

[ad#ad-1]

スポンサーリンク

 - Java