12/9 アプレットプログラミング


11/25の課題


12/2の課題


質問と解答

Q.
 イベント処理のところで最後に、

// GUI部品と,Event Listenerを関連づける。 KeyAdapterは,KeyListenerを
// 実装して中身は何もないクラス。new クラス名(){} で,「クラス名」の名前の
//ない子クラスを定義すると同時にインスタンスを作る。

    addKeyListener(new KeyAdapter(){


と中身は何もなく、名前もない子クラスを定義する方法を学びましたが、
GUI部品の組み合わせででてきた

 // window内部に GUI部品を置くときのレイアウトをFlowLayoutに指定
    window.setLayout(new FlowLayout()); 
       // 「Button1」というラベルを持つボタンを作り,window中に配置
    window.add(new Button("Button1"))

のwindow.setLayout(new FlowLayout())やwindow.add(new
Button("Button1"))も同じことなのですか?だとしたらFlowLayoutやButtonは
クラスとして名前を持っていることにはならないのですか?また、違うなら、
式 windowの動的メソッドsetLayout、addへ、引数としてnew FlowLayout()や
new Button("Button1")を渡しているとしたら、このnew 〜()という形は何で
しょう?

A.(小田原さん)
結論からいうと、違います。

前者は 「KeyAdapterのサブクラスである無名クラス」のインスタンス を生成
しており、後者はそれぞれFlowLayoutクラス、Buttonクラス(つまり名前のつ
いたクラス)のインスタンス を生成しています。

# > と中身は何もなく、名前もない子クラス
# とありますが、「中身が何もない」のはKeyAdapterクラスの方で、今定義した
# 「KeyAdapterの子クラスとしての無名クラス」にはもちろん今定義した分の
# 中身が付け加わっています。

# 「中身が何もない」ということについて誤解を招かないように補足します
# と、中身が何もないのはKeyAdapterの中のメソッドのことで、KeyAdapterク
# ラスは空ではなくて、メソッドの定義(だけ)が書かれています。APIのソー
# スを見てみますと、KeyAdapterクラスの定義は下のようになっています。

public abstract class KeyAdapter implements KeyListener {
    /**
     * Invoked when a key has been typed.
     * This event occurs when a key press is followed by a key release.
     */
    public void keyTyped(KeyEvent e) {}

    /**
     * Invoked when a key has been pressed.
     */
    public void keyPressed(KeyEvent e) {}

    /**
     * Invoked when a key has been released.
     */
    public void keyReleased(KeyEvent e) {}
}

> また、違うなら、 式 windowの動的メソッドsetLayout、addへ、引数として
> new FlowLayout()やnew Button("Button1")を渡しているとしたら、このnew 
> 〜()という形は何でしょう?

new 〜() というのは、今までも使ってきたと思いますが、コンストラクタに
よってクラスのインスタンスを生成する形です。今までは、例えば

Panel p = new Panel();

という風に、右辺で生成したインスタンスをすぐにそのクラスの変数に代入し
ていましたが、今回の使い方では変数に代入していないということです。
変数に代入しても、その後使うことがないのでこのような書き方が可能という
ことですね。もちろん変数に代入しても問題はなく、

window.setLayout(new FlowLayout());
は、
FlowLayout fl = new FlowLayout();
window.setLayout(fl);
と書くのと同じです。

わざわざ別の変数を用意してそれに代入するという手間を省いていると見るこ
ともできるでしょう。

質問に対する答えは以上です。あとは蛇足です。

無名クラスについて少し説明します。
無名クラスは必ず使わなければならないのではなく、普通に名前のあるクラス
としてキーリスナを定義することもできます。そのように
http://lecture.ecc.u-tokyo.ac.jp/~ktanaka/programming03/1111-2.html の
一番下にあるプログラムを書こうとすると下のようになります。

public class MouseDraw extends Frame{

       //  他のフィールド、メソッド定義は省略

       public MouseDraw(String title){

		// 他の初期化の処理

		MyKeyAdapter ma = new MyKeyAdapter();
		addKeyListener(ma);
		// 上2行はまとめて 
		// addKeyListener(new MyKeyAdapter()); でも同じ
       }
}

class MyKeyAdapter extends KeyAdapter{
      public void keyPressed(KeyEvent e){
	int key=e.getKeyChar();
	System.out.println("keyPressed("+e+","+key+")");
	if(key=='q') System.exit(0);
      }
}

KeyAdapterを継承した新しいクラスを、わざわざ"MyKeyAdapter"という名前を
つけて定義しています。そして、MouseDrawの中でそのクラスのインスタンス
を生成し、キーリスナとして登録しています。

この辺りの面倒な書き方を大胆に省略し、全てを addKeyListener()メソッド
の引数のカッコの中で行なってしまうのが、無名クラスを用いた方法だと思っ
てよいでしょう。

無名クラスの形式は、だいぶ省略した形になっていて、
new KeyAdapter(){...} 
とすることでキーアダプタのサブクラスの定義をしていることになっています。
そういう書き方もできる、ということだと思って下さい。

なお、どちらの書き方をしても用いるクラスの数は同じです。ただ無名になっ
て、別の場所に定義を書かなくてもすんだというだけの話です。

コンパイル後にディレクトリをみてみると、

MouseDraw$1.class
MouseDraw$2.class 
…
という$1、$2などとついたクラスが生成されていると思います。これは実は無
名クラスがコンパイルされたものを表しています。

Q.
コンパイル時に以下のエラーがでました。


Kadai1125.java:53: メソッドの宣言が不正です。戻り値の型が必要です。
                repaint();
                ^
Kadai1125.java:57: 'class' または 'interface' がありません。
    public static void main(String[] args) throws IOException{
                  ^
Kadai1125.java:93: 'class' または 'interface' がありません。
}
^
Kadai1125.java:94: 'class' または 'interface' がありません。

^


最初のは今までは通っていたと思うのですが文をちょっといじったら通らなくなりました。あとの3つは何が言いたいのかわかりません。どういうことなのでしょうか、教えてください。


以下がひっかかったプログラムです;


   // AWTを使うので java.awt.*を import する
import java.awt.*;
  // イベント駆動関係のクラスを用いるため
import java.awt.event.*;
//入力をうけるため
import java.io.*;
import java.util.*;

class Arc{
    public int here_x,here_y;
    public Arc(int x1,int x2,int x3,int x4,int x5,int x6){
	here_x=x1;
	here_y=x2;
    }
}

  // 独立したウィンドウを開くので,Frameクラスのサブクラスにする
class Kadai1125 extends Frame{
    Vector arcArray;
  
    int x;
    int y;
    
  public Kadai1125(String title){
    super(title);
    arcArray=new Vector();
      // GUI部品と,Event Listenerを関連づける
    // KeyAdapterは,KeyListenerを実装して中身は何もないクラス
    // new クラス名(){} で,「クラス名」の名前のない子クラスを定義すると同時に
    // インスタンスを作る
    addKeyListener(new KeyAdapter(){
	    public void keyPressed(KeyEvent e){
		int key=e.getKeyChar();
		System.out.println("keyPressed("+e+","+key+")");
		if(key=='q') System.exit(0);
	    }
	});
    addMouseListener(new MouseAdapter(){ // これを加える
	    public void mouseClicked(MouseEvent e){
		
		    int mx=e.getX(),my=e.getY();
		    System.out.println("mouseClicked("+e+","+mx+","+my+")");
		    arcArray.addElement(new Arc(mx,my,7,7,0,360));
		    Arc l=(Arc)arcArray.elementAt(arcArray.size()-1);
		    l.here_x=mx;
		    l.here_y=my;
		    
		} // add
		repaint();
	});
	}
  }
    public static void main(String[] args) throws IOException{
	Kadai1111 frame=new Kadai1111("Kadai1111");
	//BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
     
	//String s=br.readLine();
	//frame.x=Integer.parseInt(s);
	//String n=br.readLine();
	//frame.y=Integer.parseInt(n);
	frame.setSize(400,400);
	frame.setVisible(true);
     }
    public void paint(Graphics g){
	int i;
	x=200;
	y=25;
	// 描画するコード
	g.setColor(Color.black);
	g.fillRect(0,0,400,150);
	g.setColor(Color.gray);
	g.fillRect(0,150,400,250);
	g.setColor(Color.white);
	g.fillArc(x-10,y+200,140,140,0,360);
	g.fillArc(x,y+130,120,120,0,360);
	g.setColor(Color.black);
	g.fillArc(x+45,y+155,3,10,0,360);
	g.fillArc(x+73,y+155,3,10,0,360);
	g.setColor(Color.yellow);
	g.fillArc(x-50,y,100,100,0,360);
	g.setColor(Color.red);
	g.drawString("A SnowMan And A Moon",250,370);
	int size=arcArray.size();
	for(i=0; i
A.
TAの小田原です。

> Kadai1125.java:53: メソッドの宣言が不正です。戻り値の型が必要です。
>                 repaint();
>                 ^
> Kadai1125.java:57: 'class' または 'interface' がありません。
>     public static void main(String[] args) throws IOException{
>                   ^
> Kadai1125.java:93: 'class' または 'interface' がありません。
> }
> ^
> Kadai1125.java:94: 'class' または 'interface' がありません。
> 
> ^
> 最初のは今までは通っていたと思うのですが文をちょっといじったら通らな
> くなりました。あとの3つは何が言いたいのかわかりません。どういうこと
> なのでしょうか、教えてください。

意味不明なエラーがでたら、最初に表示されたエラーの行の周辺を調べてみて、
一つ直したら他のエラーは無視して再コンパイルしてみる、というのがよいと
思います。
一つのケアレスミスが連鎖してエラーをひき起こし、それをjavacが律義に書
き下してくれるのでわけわからんことになりやすいのですね。


XXさんのプログラムのミスも単純なもので、53行目辺りを見るとrepaint();
がメソッドの外にでてしまっていることがわかります。

また、mainメソッドがクラスの外に出ていました。

これら二つのミスを直せば普通に動きました。修正プログラムを添付しますの
で、ミスがあるプログラムと照合してみてください。


ブロック(=例えば、中括弧で囲われた部分)の中にいれるべきものを外に出し
てしまった、というだけでエラーメッセージがずらっとだされるので注意して
ください。

こういうミスを減らすために、インデントをこまめに行なう癖をつけましょう。
プログラムの最初の行から順に、行頭でTABキーをおします。最後の行までそ
の操作を繰り返せばプログラム全体がインデントされます。
これは面倒なので、一行加えるごとにTABキーを押すようにすれば自動的にき
れいにインデントされたプログラムになります。こうすれば、ブロックの中に
あるか外にあるかはだいぶわかりやすくなります。


あと、プログラムの内容について気になったのですが、
まず
    Kadai1111 frame=new Kadai1111("Kadai1111");

ここを真っ先に直しましょう。1125を動かすつもりで、1111が動いてしまいま
す。

    public Arc(int x1,int x2,int x3,int x4,int x5,int x6){
	here_x=x1;
	here_y=x2;
    }

Arcのコンストラクタですが、ここではx1, x2しか使われてないのでx3以降を引数にとる必要はありません。

    public Arc(int x, int y){ here_x = x; here_y = y; }

と直すとよいでしょう。
また、
    arcArray.addElement(new Arc(mx,my,7,7,0,360));
    Arc l=(Arc)arcArray.elementAt(arcArray.size()-1);

ここですが、Arcを生成して、それをarcArrayに入れておいてから、それを取
り出して変数に代入するというのはちょっと無駄があります。

ここは、(コンストラクタを上記の通りに修正したものとします)
    Arc l = new Arc(mx, my);
    arcArray.addElement(l);
とするのが普通です。

また、 
    l.here_x = mx;
    l.here_y = my;
というのは Arc(mx, my)の中で行なわれていることなので必要ありません。

ここまで直すと、実はmouseClickedの中ではlという変数は不要になります。
最終的には

   public void mouseClicked(MouseEvent e){
     int mx = e.getX(), my = e.getY();
    System.out.println("mouseClicked("+e+","+mx+","+my+")");
    arcArray.addElement(new Arc(mx, my));
    repaint();    
   }
    
と、だいぶ簡潔になります。

添付したプログラムは全て直してありますので参考にしてください。

プログラムを修正してから動かす際、もしうまく動かなければ既存のクラスファ
イルを消しましょう。
Arc.classが既に存在している場合、Kadai1125.javaをコンパイルしたときに
一緒にコンパイルされないようです。

% cd ~/java
% rm Arc.class

とします。他のjavaファイルを消さないように注意してくださいね。

(以降emacsの小技の解説です)

まとめてインデントしたいときは、emacsではインデントしたい部分を選択し
て

ESC x indent-region

と入力します。こうすると選択領域(リージョン)が一気にインデントされます。

マウスでいーまっくすのテキスト上をドラッグすると、ドラッグされた部分に
色がつきますが、これが選択領域(リージョン)です。

キーボードだけでリージョンを選択することもできます。Ctrl キーを押しな
がらspaceを押すと、カーソルの位置に「マーク」がセットされます(ミニバッ
ファに"Mark set"と表示されるはず)。マークしたあとは普通に操作して問題
ありませんが、「マーク」と「現在のカーソル位置」の間の領域が、見た目は
何も変わっていませんが、実はリージョンということになっているのです。

リージョンは使いこなせればびっくりするほど便利です。例えば、Altキーを
押しながらwと入力すると、リージョンがコピー(キルリングセーブ)されます。
Ctrlキーを押しながらw と入力すると、リージョンがカット(キル)されます。

# ちなみにCtrlキーを押しながらyと押すと、コピーされた部分がペースト(ヤ
# ンク)されます。

ぜひ使いこなして、プログラムの開発効率をあげて下さい。


Q.
GUI部品のイベント処理で
 
  Button button1, button2, button3;
  Checkbox check1, check2;
  TextField textField;
  GUITest2(){
    setLayout(new FlowLayout());
       // 「Check1」というラベルを持つチェックボックスを作り,window中に配置
    add(check1=new Checkbox("Check1"));

として、Buttonクラスなどのインスタンス変数をあらかじめ宣言してからコンストラクタ内でadd(check1=new Checkbox("Check1"))のようにしていますが、前の章では

public class GUITest extends Frame {
  public static void main(String args[]){
       // GUITestクラスのインスタンスを生成
    GUITest window=new GUITest(); 
       // window内部に GUI部品を置くときのレイアウトをFlowLayoutに指定
    window.setLayout(new FlowLayout()); 
       // 「Button1」というラベルを持つボタンを作り,window中に配置
    window.add(new Button("Button1"));

というように、部品の作り方が違うと思うのですがどうしてですか?

mousePressed(MouseEvent e)メソッドの中で、
    
     // イベントの処理をおこなったことを表すため true を返す

この文何を示しているのかがわかりません。

actionPerformed(ActionEvent e)メソッドで

     // イベントのターゲットは GUI 部品のオブジェクトだが,
     // これが Button クラスのインスタンスかどうかのチェック
    System.out.println(e);
    Object source=e.getSource();
    if(source instanceof Button)

System.out.println(e)が何を表示させる何ための文かが分かりません。そのあとは、Objectクラスのsourceという変数に「イベントのターゲットがどんなインスタンスかを示す変数」を代入していると思うのですが、eというオブジェクトにgetSource()メソッドを使うとどうしてそうなるのかが分かりません。

 // イベントのターゲットが Checkbox クラスのインスタンスかどうかのチェック
    else if(source instanceof Checkbox){
        // その時は チェックボックスの内容を表示
      message="Checkbox"+(Checkbox)source;

で、チェックボックスを押しても(イベントのターゲットとしても)画するチェックボックスの内容が表示されません。どうしてでしょう?






A.
TAの小田原です。

> GUI部品のイベント処理で
>  
>   Button button1, button2, button3;
>   Checkbox check1, check2;
>   TextField textField;
>   GUITest2(){
>     setLayout(new FlowLayout());
>        // 「Check1」というラベルを持つチェックボックスを作り,window中に配置
>     add(check1=new Checkbox("Check1"));
> 
> として、Buttonクラスなどのインスタンス変数をあらかじめ宣言してからコンストラクタ内でadd(check1=new Checkbox("Check1"))のようにしていますが、前の章では
> 
> public class GUITest extends Frame {
>   public static void main(String args[]){
>        // GUITestクラスのインスタンスを生成
>     GUITest window=new GUITest(); 
>        // window内部に GUI部品を置くときのレイアウトをFlowLayoutに指定
>     window.setLayout(new FlowLayout()); 
>        // 「Button1」というラベルを持つボタンを作り,window中に配置
>     window.add(new Button("Button1"));
> 
> というように、部品の作り方が違うと思うのですがどうしてですか?

インスタンス変数を用意しているか、そうでないかの違いがありますね。
前者はわかりやすいようにインスタンス変数を宣言していますが、後者ではそ
れを省いています。

実際、部品を生成するだけならインスタンス変数を用意する必要はありません。
変数に代入するというのはオブジェクトに名前をつけているようなものですが、
他の部分でその名前を使うことがなければわざわざ変数を用意してやることは
ないのです。後者はまさにそういう場合です。

一方、前者においては、Checkbox check1, check2; という二つの変数が、
actionPerformedの中で用いられています。

      else if(label.equals("Button1")){
          // その時は表示する messageは "Button1"
        message="Button1," + "Check1 is "+check1.getState();
      }
      else{
          // それ以外の時は表示する messageは "Unknown Button"とする.
        message="Button2," + "Check2 is "+check2.getState();
      }

このプログラムではCheckboxのインスタンスを参照する必要が生じたため、
check1, check2という名前をつけてやっているということです。

なお他の変数はどこにも使われていないので、後者と同様にインスタンス変数
を用いずに生成して構いません。

http://lecture.ecc.u-tokyo.ac.jp/~ktanaka/programming03/1202-3.html の
CanvasTestクラスでは、必要なdeleteButton, clearButtonのみをイン
スタンス変数として宣言し、他の部品については変数を用意せずにプログラム
が書かれていることがわかると思います。

なぜdeleteButton, clearButtonが必要かというと、actionPerformedの

    // Deleteボタンが押された時
    if(source.equals(deleteButton)){
      myCanvas.deleteLine();
    }
    // Clearボタンが押された時
    else if(source.equals(clearButton)){
      myCanvas.clearLine();
    }

の部分で使うからですね。

> mousePressed(MouseEvent e)メソッドの中で、
>     
>      // イベントの処理をおこなったことを表すため true を返す
> 
> この文何を示しているのかがわかりません。

これは多分コメントの消し忘れですね…。気にしないでください。

> actionPerformed(ActionEvent e)メソッドで
> 
>      // イベントのターゲットは GUI 部品のオブジェクトだが,
>      // これが Button クラスのインスタンスかどうかのチェック
>     System.out.println(e);
>     Object source=e.getSource();
>     if(source instanceof Button)
> 
> System.out.println(e)が何を表示させる何ための文かが分かりません。

System.out.println()というメソッドは大変便利なメソッドで、String型はも
とより、boolean型であろうがint型であろうが、配列であろうがjavaが持って
いるクラスのオブジェクトであればだいたい「そのオブジェクトを表す情報」
を表示してくれます。

というのは、オブジェクトはtoString()というメソッド
を持っており、これを呼び出すことで文字列に変換することができるからです。
System.out.println()ではこの文字列を表示します。

eについても例に洩れず、ActionEventクラスのオブジェクトの情報が表示され
るはずです。ものは試し、いろいろコードを書いてSystem.out.println()を
使ってみましょう。

> そのあとは、Objectクラスのsourceという変数に「イベントのターゲットが
> どんなインスタンスかを示す変数」を代入していると思うのですが、eとい
> うオブジェクトにgetSource()メソッドを使うとどうしてそうなるのかが分
> かりません。

e は ActionEventクラスのインスタンスを参照している変数です。つまり、e 
はActionEventクラスの情報とメソッドを持っているということです。
ActionEventクラスは、そのイベントのソース(発生元)のオブジェクトが何で
あるかという情報(要はオブジェクトの参照)を蓄えています。getSourceとい
うメソッドで、そのイベントのソースであるオブジェクトへの参照を返しているのです。

例えば、ボタン(bさん)がクリックされたとします。このとき、bさんはイベン
トオブジェクト(e君)を生成しますが、このときにe君は自分を生み出したbさ
んを指し示す矢印を持っておきます。e君は予め登録されていたリスナのオブ
ジェクトに渡されます。そこで適切なメソッドによって処理されるのですが、
ここでgetSourceが実行されると、e君はbさんを指し示す矢印を返す、というわ
けです。

われながら比喩がへたくそです。すみませんm(._.)m 

でも、イベントe君と、bさんを指す矢印というのは重要です。矢印は携帯の番
号の比喩でもいいです。矢印とは実際は「参照」のことです。オブジェクトA
がある他のオブジェクトBの参照を持つことができれば、AはBを利用すること
ができます。

>  // イベントのターゲットが Checkbox クラスのインスタンスかどうかのチェック
>     else if(source instanceof Checkbox){
>         // その時は チェックボックスの内容を表示
>       message="Checkbox"+(Checkbox)source;
> 
> で、チェックボックスを押しても(イベントのターゲットとしても)画するチェックボックスの内容が表示されません。どうしてでしょう?

チェックボックスが生成するイベントはItemEventであり、ActionEventではあ
りません。したがって、ActionEventのソース(発生元)がチェックボックスに
なることはありえません。

チェックボックスのItemEventは、ItemListenerの
itemStateChanged(ItemEvent ie)というメソッドで処理されますので、このメ
ソッドを定義してやる必要があります。詳しくはまた次の機会に。

Q.
「画面のちらつきをなくす」で

どうしてここでは、無名の子クラスを使用せず、KeyListenerを実装しているのですか?

OffScreenプログラムは、”次に表示しようとする画面全体をオフス クリーンイメージで作成しておいて,drawImageで表示する方法”を用いているのに、下のMouseDrawプログラムと違ってダブルバッファリングでは無いのですか?

paintメソッドで
  // lineCount個,linesに登録されているLineを描画する
    for(i=0;i< lineCount;i++){
      g.drawLine(lines[i].start_x,lines[i].start_y,
                 lines[i].end_x,lines[i].end_y);
    }
の部分が良くわかりません。いつ、どこでLineがlineCount個,linesに登録されているのでしょうか?マウスで描画する際の部分下に// マウスをドラッグ中の時は
    if(dragging){
       // 赤い色で
      g.setColor(Color.red);
       // lines[lineCount] を描画する.
      g.drawLine(lines[i].start_x,lines[i].start_y,
                 lines[i].end_x,lines[i].end_y);
    }
で書いてありますよね。
A.
TAの小田原です。

> 「画面のちらつきをなくす」で
> どうしてここでは、無名の子クラスを使用せず、KeyListenerを実装しているのですか?

別にどちらでもよいのですが、イベント処理の理解を深めるため、無名クラス
よりも直接的なキーリスナーの実装を選択されたということではないかと思い
ます。

> OffScreenプログラムは、”次に表示しようとする画面全体をオフス クリーンイメージで作成しておいて,drawImageで表示する方法”を用いているのに、下のMouseDrawプログラムと違ってダブルバッファリングでは無いのですか?

どちらもオフスクリーンイメージを作成することには変わりはないのですが、
単に描画回数を減らすために用いた場合と、ちらつきをなくすために用いた場
合によって呼び方を変えているということだと思います。ちらつきをなくすた
めに用いた場合「ダブルバッファリング」と呼ぶ、ということでしょう。また、
ダブルバッファリングはupdateというメソッドを呼び出しているという点でも
単なるオフスクリーンイメージの利用とは少々異なっています。

> paintメソッドで
>   // lineCount個,linesに登録されているLineを描画する
>     for(i=0;i< lineCount;i++){
>       g.drawLine(lines[i].start_x,lines[i].start_y,
>                  lines[i].end_x,lines[i].end_y);
>     }
> の部分が良くわかりません。いつ、どこでLineがlineCount個,linesに登録されているのでしょうか?マウスで描画する際の部分下に// マウスをドラッグ中の時は
>     if(dragging){
>        // 赤い色で
>       g.setColor(Color.red);
>        // lines[lineCount] を描画する.
>       g.drawLine(lines[i].start_x,lines[i].start_y,
>                  lines[i].end_x,lines[i].end_y);
>     }
> で書いてありますよね。
> 

突然ですが、ここで問題です。

「http://lecture.ecc.u-tokyo.ac.jp/~ktanaka/programming03/1125-3.html 
のコードによるMouseDraw.classを実行し、このコード中に定義された全ての
メソッドが呼び出されてプログラムが正常終了したとき、下のメソッドまたは
コンストラクタがはじめて呼び出された順序を答えなさい。」

Line
MouseDraw 
main       
paint
KeyPressed
MousePressed
MouseDragged

この問題を考えてみれば、おのずと質問の答えが明らかになると思います。ま
た、そのときlineCountという変数の値がどう変化していくかも考えてみると
より理解がふかまるでしょう。答えは下の方に書いておきます。

上の問題の条件下では一意に決まりますが、MouseEventやKeyEvent を引数と
するメソッドは、イベントが発生すれば呼び出されるので、呼び出される順番
が静的に決まっているわけではありません。

ところで、質問文中に「Line(オブジェクト)がlineCount個、linesに登録され
る」とありますが、lineCountはあくまでも配列linesに納められている現在の
Lineオブジェクトの個数を表しているだけですので注意してください。

Q.
「Vectorクラス」の説明ではじめに書いてあるメソッドで配列を2倍にしたところまでは分かるのですが、

 int i;
      for(i=0;i< lines.length;i++)
	newLines[i]=lines[i];
      lines=newLines;

では どうしてわざわざ一つ一つの配列の成分を一致させていかないといけないのですか?ines=newLinesとさっさとやってはいけないのですか?

int size(){
    return lineCount;
  }
で内容がこれだけだったらメソッドにする必要があるのですか?

 void addElement(Line line){
    lines[lineCount]=line;
でこれはコンストラクタ呼び出しをされると、lineに引数が渡され、その値が lines[lineCount]に代入されるということですか?だとすると、lines配列のlineCountだった変数は初期化されてしまうと思うのですが?

paintクラスで
    int size=lineArray.size();
    if(dragging) size--;
    for(i=0;i< size;i++){
    Line l=lineArray.elementAt(i);

int size=lineArray.size();はlineArrayオブジェクト型でsizeメソッドを実行したのに、それを基本データ型のint sizeに代入できるのですか?同じくどうしてLine l=lineArray.elementAt(i);で配列型に代入できるのですか?どうしてマウスをドラッグするとsize--となるのですか?
A.
TAの小田原です。

> 「Vectorクラス」の説明ではじめに書いてあるメソッドで配列を2倍にしたところまでは分かるのですが、
> 
>  int i;
>       for(i=0;i< lines.length;i++)
> 	newLines[i]=lines[i];
>       lines=newLines;
> 
> では どうしてわざわざ一つ一つの配列の成分を一致させていかないといけないのですか?ines=newLinesとさっさとやってはいけないのですか?

ものは試しでやってみましょう。というかやってみましたか? 質問は大いに歓
迎ですが、まず自分で実験できることはしてみましょう。

そして、画面が真っ白になってNullPointerExceptionが起きることを確認し、
なぜこうなるのかな、と考えてみましょう。

ひょっとしたら自分なりに納得のいく解答が導けるかもしれません。そのとき
に、合っているかどうかを確認するためにメールをくれるのは問題ありません
し、わからなかったらそれでまた質問するのもOKです。

ともかく、この質問は結構いいところに気がついてると思います。配列の変数
というのは、配列型への参照をもっています。配列型というのはJava では他
のオブジェクト型と同じくオブジェクトなので、変数に納められた参照という
のは実はあくまでも参照というだけで、実体ではありません。

lines = newLines とすると、lines のもつ参照を newLines のもつ参照で置
き換えるだけなのです。しかし、newLinesの実体は、直前の行で

   Line[] newLines=new Line[lines.length*2];

と領域を確保されただけでまだ何も入っていません。ですから、この変数の代
入だけでは、linesの中身が全てnullになるだけです。

というわけで、newLines の中身を初期化するために、下のように
>       for(i=0;i< lines.length;i++)
> 	newLines[i]=lines[i];
for文で、既存のlines内のLineオブジェクトをすべて移してやる
必要があった、ということです。

> int size(){
>     return lineCount;
>   }
> で内容がこれだけだったらメソッドにする必要があるのですか?

クラスを作成する際に、sizeというメソッドを用意することにしよう、と決め
ていたので、それにしたがってメソッドを書いてみた所このようなものになっ
た、ということです。

ここではインスタンスフィールドにアクセスするメソッドの意義は強調されま
せんが、例えばlineCountがprivateな変数だった場合、publicなアクセサメソッ
ドを用意する必要があります。

javaで数千行のコードを書くようになると、クラスをたくさん設計することに
なりますが、この際インスタンスフィールドの値の設定と取得に関してはいち
いちメソッドを定義してそれを用いて行ないます。これは慣習のようなもので、
数千行程度のコードを個人の研究で書くくらいならアクセサメソッドのありが
たみを実感することも少ないのではないかと思っていますが、Javaでプログラ
ムを書く時はこうするんだ、みたいな感じで書いてもいいんじゃないかなと思
います。

>  void addElement(Line line){
>     lines[lineCount]=line;
> でこれはコンストラクタ呼び出しをされると、lineに引数が渡され、その値が lines[lineCount]に代入されるということですか?だとすると、lines配列のlineCountだった変数は初期化されてしまうと思うのですが?

LineArrayのコンストラクタ呼びだしのことでしょうか。確かにLineArrayを新
しく生成すると、lineCount=0;が実行されます。しかし、lineCountはあくま
でもインスタンス変数で、複数のLineArrayのオブジェクトがそれぞれ独自の
値を持つことができます。そのオブジェクトに関して再びコンストラクタが実
行されることはないので、lineCountが初期化されるということはありません。

LineArray la = new LineArray();
la.addElement(new Line(x, y));

などとすると、lineCountは0なので、addElementの中で 
line = new Line(x,y); lines[0] = line; 
が実行されることになります。その後すぐにlineCount
はインクリメントされるので、次回addElementしたときには 
lines[1]=line; 
が実行されます。上書きの心配はないということですね

> paintクラスで
>     int size=lineArray.size();
>     if(dragging) size--;
>     for(i=0;i< size;i++){
>     Line l=lineArray.elementAt(i);
> 
> int size=lineArray.size();はlineArrayオブジェクト型でsizeメソッドを
> 実行したのに、それを基本データ型のint sizeに代入できるのですか?

メソッド定義の際、必ず戻り値を明示していますよね。メソッドを実行すると、
戻り値がvoidでない限りはなんらかの値を返すことになります。そのようなメ
ソッドのメソッド呼びだしは、戻り値の型の値と同一視してもよいことになる
のです。

例えば

int getX(){
    return x;
}

というメソッドがあれば、getX()はint型の値とみなせます。

int mx = getX();

とすることもできるわけです。

> 同じくどうしてLine l=lineArray.elementAt(i);で配列型に代入できるのですか?

LineはLine型で、配列型ではありません。ここでは、lineArrayというオブジェ
クト型に納められているi番目のデータをLine型のlに代入しているわけですね。

lineArrayのstaticメソッドelementAtは戻り値は

>  Line elementAt(int index){
>    return lines[index];
>  }

上の通り Line です。さっきと同じく、elementAtメソッドはLineのオブジェ
クトと同一視できます。

> どうしてマウスをドラッグするとsize--となるのですか?

さてなんででしょうね。これは考えてみて欲しいと思います。
ちょっとその周辺のコードを良く読んでみたらわかります。
どうしてもわからなければまたメール下さい。

今日の課題

上のURLは,今日(12/9)の17:00 までは
Forbidden

You don't have permission to access /~ktanaka/programming03/kadai1209.html on this server.
とうメッセージが出てアクセ スできないはずである.17:00以降にも同様のエラーが出る時は,Shiftキーを 押しながら,再読み込み(Reload)を押してみること.

課題の締切りは12/24(水)の21時まで(23日は祝日のため)


次へ進む