クラスとメソッド

59
第7章
動くブロックをたくさん作ってみよう!!
¶
³
重要
• クラス (class)
– フィールド (field): 属性
– メソッド (method): ふるまい
• オブジェクト (Object)
– インスタンス (instance) とも呼ぶ
– 具体的なもの
• メソッド = ブロック + 名前
[メソッド修飾子] 返り値型 メソッド名 ([型 仮引数, . . .]) ブロック
– メソッド呼び出し
メソッド名 ([式, . . .])
– コンストラクタ (constructor) ... 初期化を行うメソッド
• オーバーロード (overload) ... メンバの多重定義
• 配列
– 宣言:
型 [ ] 配列名;
– 生成:
new 型 [ 整数式]
– 宣言と生成を同時に:
型 [] 配列名 = new 型 [ 整数式];
– 配列の宣言,生成,初期化を同時に:
µ
– 整数式番目の要素へのアクセス:
型 [] 配列名 = { 値, 値, . . .};
´
配列名 [ 整数式 ]
7.1 ブロックの基本的な属性をまとめてみよう!!
例題 7.1.1 次の 3 種類のブロックが壁に反射しながら水平方向に等速直線運動するプログラムを作成せ
よ.
幅
高さ
初期位置 (x, y)
x 方向の初速度
ブロック 1
30
10
(0, 50)
1
ブロック 2
10
10
(width - 10, 80)
-2
ブロック 3
50
10
(width / 2, 110)
2
ただし,width は,描画領域の幅である.
2
プログラム 7.1.1 では,3 つのブロックに対して,ブロックの幅,高さ,位置 (x, y),速度を,それぞれ,x1, y1,
第 7 章 動くブロックをたくさん作ってみよう!!
60
w1, h1, vx1,x2, y2, w2, h2, vx2,x3, y3, w3, h3, vx3 という変数を用いて取り扱っている.このような方
法では,ブロックの数が増えるにつれて扱う変数が膨大になり,プログラムも煩雑になってしまう.同様に,21 行目
から 26 行目では,3 つのブロックそれぞれに対して,壁との衝突判定を行い,衝突した場合には,速度を反転させる処
理を行っており,ブロックの数が多くなった場合や処理の種類が多くなった場合には,プログラムが煩雑になってしま
う.このような場合,それぞれのブロックの共通なデータの種類や働きを一つにまとめることでプログラミングが容易
になる.オブジェクト指向プログラミング言語では,クラス (class) という概念を用いることによって実現している.
プログラム 7.1.1 水平方向に等速直線運動する 3 つのブロック (RectMove4.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class RectMove4 extends JApplet {
Dimension d;
int x1, y1, vx1, w1, h1;
int x2, y2, vx2, w2, h2;
10
int x3, y3, vx3, w3, h3;
public void init(){
d = getSize();
w1 = 30; h1 = 10; x1 = 0; y1 = 50; vx1 = 1;
15
w2 = 10; h2 = 10; x2 = d.width − w2; y2 = 80; vx2 = −2;
w3 = 50; h3 = 10; x3 = d.width / 2; y3 = 110; vx3 = 2;
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (x1 < 0 | | x1 + w1 > d.width) vx1 *= −1;
20
x1 += vx1;
if (x2 < 0 | | x2 + w2 > d.width) vx2 *= −1;
x2 += vx2;
if (x3 < 0 | | x3 + w3 > d.width) vx3 *= −1;
25
x3 += vx3;
repaint();
}
}
}).start();
30
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
g.drawRect(x1, y1, w1, h1);
35
g.drawRect(x2, y2, w2, h2);
g.drawRect(x3, y3, w3, h3);
}
}
2
オブジェクト指向では,オブジェクトを基本的な構成単位として用いている.オブジェクトとは,具体的な実体そ
のものであり,例題 7.1.1 では,一つのブロックが一つのオブジェクトを表すと考えることができる.オブジェクトに
7.1 ブロックの基本的な属性をまとめてみよう!!
61
は,そのオブジェクトの属性や状態を表すデータとオブジェクトに固有な操作があり,それぞれ,フィールド (field),
メソッド (method) と呼んでいる.ブロックを一つのオブジェクトと考えると,個々のブロックによって様々な値を持
つデータ,つまり,ブロックの属性としては,ブロックの幅 width,高さ height,位置 (x, y),速度 vx が挙げられ
る.ここでは説明を簡単にするために属性についてだけ考え,ブロックに対する操作については次小節で説明する.
このようなオブジェクトの設計図の役割を果たすものがクラスである.クラスはオブジェクトがどのようなものかを
プログラム上で表現する一まとまりであり,オブジェクトに関するデータとオブジェクトの振る舞いが記述されてい
る.Java では,フィールドとメソッドを総称してメンバと呼んでいる.また,オブジェクトはインスタンス (instance)
とも呼ばれる.
7.1.1 クラスの宣言
¥
¨
[クラス修飾子] Class クラス名 { ... }
§
¦
クラス修飾子には,次のようなものがある.
1. public ... 他のパッケージからアクセス可,public がないとアクセス不可
2. abstract ... 抽象クラス.抽象クラスは抽象メソッドを持つクラス.抽象メソッドとは,メソッドの本体を定義
せず,メソッド名,引数とその型,戻り値型,メソッドが投げる例外だけを宣言したもの.
3. final ... 継承禁止
7.1.2 オブジェクトの生成
¨
§
new クラス名 ([引数])
¥
¦
クラスはオブジェクトの性質を定義しているだけであるので,個々のオブジェクトを扱うためには,オブジェクトを
生成する必要がある.オブジェクトの生成には,new 演算子を用いる.
7.1.3 フィールドの初期値
フィールドや配列要素に初期値が無い場合には,デフォルトの初期値が設定される.ただし,局所変数は初期値が設
定されないので注意すること.それぞれのデフォルト値は表表 7.1 のようになっている.
表 7.1
フィールドの初期値
型
初期値
型
初期値
boolean
false
int
0
double
0.0
参照型
null
メンバにアクセス修飾子を付けることで,外からメンバを隠すことが可能である.定数ではないフィールドは隠すこ
とが一般的であり,安全である.アクセス修飾子には次のようなものがある.
•
•
•
•
public ... クラスがアクセスできる範囲ならば,どこでもアクセス可
private ... クラス内でのみアクセス可
protected ... サブクラスとパッケージ内でのみアクセス可
なし ... パッケージ内でのみアクセス可
例題 7.1.2 プログラム 7.1.1 をフィールドだけを持つクラスを用いて書き直せ.
2
第 7 章 動くブロックをたくさん作ってみよう!!
62
プログラム 7.1.2 水平方向に等速直線運動する 3 つのブロック (フィールドだけのクラス)(Rect-
Move5.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class RectMove5 extends JApplet {
Dimension d;
class Block {
10
int x, y; // position
int width, height; // width, height
int vx; // velocity
}
Block block1, block2, block3;
15
public void init(){
d = getSize();
block1 = new Block(); block2 = new Block(); block3 = new Block();
block1.width = 30; block1.height = 10; block1.x = 0; block1.y = 50; block1.vx = 1;
block2.width = 10; block2.height = 10;
20
block2.x = d.width − block2.width; block2.y = 80; block2.vx = −2;
block3.width = 50; block3.height = 10;
block3.x = d.width / 2; block3.y = 110; block3.vx = 2;
25
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
if (block1.x < 0 | | block1.x + block1.width > d.width)
block1.vx *= −1;
block1.x += block1.vx;
if (block2.x < 0 | | block2.x + block2.width > d.width)
30
block2.vx *= −1;
block2.x += block2.vx;
if (block3.x < 0 | | block3.x + block3.width > d.width)
block3.vx *= −1;
35
block3.x += block3.vx;
repaint();
}
}
}).start();
40
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
g.drawRect(block1.x, block1.y, block1.width, block1.height);
45
g.drawRect(block2.x, block2.y, block2.width, block2.height);
g.drawRect(block3.x, block3.y, block3.width, block3.height);
}
}
2
7.2 ブロックの基本的なふるまいをまとめてみよう!!
63
10 行目から 14 行目が,Block クラスの定義である.この例では,フィールドだけを定義している.ブロックに必要
な属性は,ブロックの位置 (x, y),幅 width,高さ height,水平方向の速度 vx であるので,この 5 つの変数をメン
バとして定義する.
15 行目は,3 つの Block オブジェクトを参照するための変数を宣言している.この行では,まだ,オブジェクトを
生成しておらず,オブジェクトを参照するだけの変数を用意しているにすぎない.
19 行目では,new 演算子を用いて,3 つのブロックオブジェクトを生成し,それぞれ,block1, block2, block3
という変数で参照している.
20 行目から 24 行目で,各オブジェクトのデータを設定している.オブジェクトのメンバへのアクセスはピリオド
(.) を用いてアクセスする.たとえば,一つ目のブロックの幅は,block1.width で扱うことができる.
28 行目以降,プログラム 7.1.1 で x1, y1 等の変数を用いて扱っていたものを,オブジェクトのメンバにアクセスす
ることで,データを取り扱っている.
7.2 ブロックの基本的なふるまいをまとめてみよう!!
メソッド (method) とは,なんらかの処理のまとまりであるブロックに名前を付けた構文単位である.例題 7.1.2 で
は,ブロックを一つのオブジェクトとして表し,ブロックの属性だけに注目してクラスを作成した.ここで,ブロック
に対する処理を考えると,例題 7.1.1 では,ブロックが各単位時間に移動する処理とブロックを描画する処理が必要に
なる.したがって,これらの処理をまとめてメソッドとすることで,プログラム 7.1.1 がさらに分かりやすくなる.
• メソッド宣言
²
¯
[メソッド修飾子] 返り値型 メソッド名 ([型 仮引数, . . .])
±
ブロック
• メソッド呼び出し
¨
¥
メソッド名 ([式, . . .])
§
¦
°
7.2.1 引数
引数とは,あるメソッドから別なメソッドを呼び出す時に,呼び出すメソッドが呼び出されるメソッドに受け渡す値
のリストである.引数があるメソッドの宣言では,引数の型と数に対応した引数の宣言が必要になる.
7.2.2 戻り値
戻り値とは,あるメソッドで処理した値を呼び出したメソッドに返す値である.メソッドから値を返す場合には,
return 文を用いる.
¨
¥
return [式];
§
¦
戻り値があるメソッドの宣言には,その戻り値の型と一致した戻り値型を宣言する必要がある.
7.2.3 コンストラクタ (constructor)
コンストラクタとは,オブジェクトを初期化する特別なメソッドであり,クラス名と同じ名前を持ち,引数を受け取
ることはできるが,戻り値型は宣言しない.
第 7 章 動くブロックをたくさん作ってみよう!!
64
7.2.4 オーバーロード (overload)
オーバーロードとは,引数の個数と型が異る同じ名前のメソッドやコンストラクタを複数宣言することである.
例題 7.2.1 プログラム 7.1.2 をメソッドも持つクラスを用いて書き直せ.
2
10 行目から 31 行目で Block クラスの宣言を行っている.プログラム 7.1.2 との違いは,2 種類のコンストラクタ
Block と move メソッド,draw メソッドを宣言していることである.
15 行目から 17 行目のコンストラクタ Block では,引数を取らすにオブジェクトを生成した場合の処理が記述して
おり,全てのフィールドを 0 に初期化する処理を行っている.
19 行目から 21 行目のコンストラクタ Block では,与えられたブロックの位置,速度,幅,高さでブロックの生成お
よび初期化を行う.
24 行目から 27 行目では,ブロックが単位時間に移動する処理をメソッドとして宣言している.ここでは,現在のブ
ロックが描画範囲からはみ出た場合に速度 vx の符号を反転する処理と,単位時間毎のブロックの移動を計算する処理
が記述されている.
28 行目から 30 行目では,ブロックの描画を行うメソッド draw を宣言している.このメソッドでは,Graphics の
変数 g を引数として受け取り,その g に対して,描画処理 drawRect() を行っている.
コンストラクタを用意することで,36 行目から 38 行目のように,初期化を伴うオブジェクトの生成を簡単に行
うことができる.また,ブロックの移動処理は,move メソッドで記述しているので,42 行目のように,各ブロック
block1, block2, block3 に対して move メソッドを呼び出すことによって実現することができる.同様に,ブロッ
クの描画も 50 行目に示すように,各ブロックに対して draw メソッドを呼び出すことで実現できる.
例題 7.2.2 プログラム 7.2.1 の Block クラスに setBlockColor メソッドを追加し,ブロックに色を付け
よ.(public void setBlockColor(int r, int g, int b))
2
7.2 ブロックの基本的なふるまいをまとめてみよう!!
65
プログラム 7.2.1 水平方向に等速直線運動する 3 つのブロック (完全なクラス)(RectMove6.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.*;
5
public class RectMove6 extends JApplet {
Dimension d;
class Block {
10
int x, y; // position
int width, height; // width, height
int vx; // velocity
Block(){ // constructor
x = y = width = height = vx = 0;
15
}
Block(int px, int py, int pvx, int w, int h){ // constructor
x = px; y = py; width = w; height = h; vx = pvx;
20
}
public void move(){
if (x < 0 | | x + width > d.width) vx *= −1;
x += vx;
25
}
public void draw(Graphics g){
g.drawRect(x, y, width, height);
}
}
30
Block block1, block2, block3;
public void init(){
d = getSize();
35
block1 = new Block(0,
50, 1, 30, 10);
block2 = new Block(d.width − 30, 80, −2, 10, 10);
block3 = new Block(d.width / 2, 110, 2, 50, 10);
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
40
block1.move(); block2.move(); block3.move();
repaint();
}
}
}).start();
45
public void paint(Graphics g){
g.clearRect(0, 0, d.width, d.height);
block1.draw(g); block2.draw(g); block3.draw(g);
}
50
}
2
第 7 章 動くブロックをたくさん作ってみよう!!
66
7.3 様々な動きをする多数のブロックを作ってみよう!!
例題 7.3.1 プログラム 7.2.1 のオブジェクトを配列を用いて表せ.
2
変数 (variable) は一つのデータを入れる入れ物の役割を果しているが,配列 (array) は同じデータ型の複数のデータ
をまとめたものである.
a[0]
a[1]
a[2]
a[3]
a[4]
b
3
6
9
10
11
1
図 7.1
配列と変数
配列には番号が付いておりこの配列の番号を添字 (index) と呼び,それぞれ保存されている値を配列の要素 (element)
¨
¥
と言う.添字は 0 から始まり,0 番目の要素,1 番目の要素と数える.配列は,次のように宣言,生成する.
§
¦
– int[] a; // int 型の配列 a を宣言
¨
¥
• 整数式個の要素を持つ配列を生成:
new 型 [ 整数式]
§
¦
– a = new int[5]; // 要素が 5 個の int 型の配列を生成
• 配列の宣言:
•
•
•
•
型 [ ] 配列名;
– new は,ある型のデータが整数式個入るメモリを新しく確保するという Java の予約語である.
¨
¥
配列の宣言と生成を同時に行う:
型 [] 配列名 = new 型 [ 整数式];
§
¦
– int[] a = new int[5]; // 要素が 5 個の int 型の配列の宣言と生成
¨
¥
配列の宣言,生成,初期化を同時に行う:
型 [] 配列名 = { 値, 値, . . .};
§
¦
– int[] a = {1, 2, 3};
¨
¥
配列の整数式番目の要素へのアクセス:
配列名 [ 整数式 ]
§
¦
– a[0]
¨
¥
配列の大きさを返す式:
配列名.length
§
¦
– a.length
上記の配列の構文を変数と比較しながらまとめたものを表 7.2 に示す.
7.3.1 配列使用上の注意事項
1. 配列の添字は整数式に限る.
•
•
•
•
a[1]
int i; ... a[i]
int i; ... a[i++]
int i; ... a[i - 1]
2. 宣言した要素数を越えて配列にアクセスしない.
• int[] a = new int[5]; のとき a[0] から a[4] までしかアクセスできない.a[5] へのアクセスは実行
時エラーとなる.
3. 配列の大きさは固定で,生成後に大きさの変更はできない.
4. 配列を宣言しただけでは,配列は生成されない.
• new 演算子を用いて配列の領域を確保する必要がある.
5. 配列どうしの演算はできない.
7.3 様々な動きをする多数のブロックを作ってみよう!!
67
表 7.2 配列と変数の関係
宣言
生成
宣言と生成
宣言と初期化
配列の整数式番目の要素へのアクセス
配列の大きさ
配列
変数
型 [ ] 配列名;
型
int[] a;
int a;
new 型 [ 整数式]
—
a = new int[5];
—
型 [] 配列名 = new 型 [ 整数式];
—
int[] a = new int[5];
—
型 [] 配列名 = { 値, 値, . . .};
型 変数名 = 値;
変数名;
int[] a = {1, 2, 3};
int a = 1;
配列名 [ 整数式 ]
—
a[0]
—
配列名.length
—
a.length
—
• int[] a = new int[2], b = new int[2]; で a + b という演算はできない.
6. 配列要素どうしの演算は可能.
• int[] a = new int[2], b = new int[2]; で a[0] + b[1] という演算が可能.
31 行目では,blocks という名前の配列の宣言を行っている.32 行目は,ブロックの個数を表す変数を宣言し与え
ている.配列の各要素を生成するために,38 行目から 40 行目の処理を行っている.配列を利用することで,44 行目
の各ブロックの移動や 52 行目のブロックの描画が繰り返し処理で行えるようになっている.
例題 7.3.2 次の 5 種類のブロックが壁に反射しながら水平方向に等速直線運動するプログラムを作成せ
よ.
幅
高さ
初期位置 (x, y)
x 方向の初速度
ブロック 1
5
10
(0, 0)
1
ブロック 2
10
10
(20, 30)
2
ブロック 3
15
10
(40, 60)
3
ブロック 4
20
10
(60, 90)
4
ブロック 5
25
10
(80, 120)
5
(値の規則性を利用すると良い.)
2
第 7 章 動くブロックをたくさん作ってみよう!!
68
プログラム 7.3.1 水平方向に等速直線運動する 3 つのブロック (配列利用)(RectMove8.java)
import javax.swing.JApplet;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.*;
5
public class RectMove8 extends JApplet {
Dimension d;
class Block {
int x, y; // position
10
int width, height; //
int vx; // velocity
Block(){ // constructor
x = y = width = height = vx = 0;
15
}
Block(int px, int py, int pvx, int w, int h){ // constructor
x = px; y = py; width = w; height = h; vx = pvx;
}
20
public void move(){
if (x < 0 | | x + width > d.width) vx *= −1;
x += vx;
}
25
public void draw(Graphics g){
g.drawRect(x, y, width, height);
}
}
Block[ ] blocks;
30
final int num blocks = 3;
public void init(){
blocks = new Block[num blocks];
35
d = getSize();
block[0] = new Block(0,
50, 1, 30, 10);
block[1] = new Block(d.width − 30, 80, −2, 10, 10);
block[2] = new Block(d.width / 2, 110, 2, 50, 10);
40
new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e){
for(int i = 0; i < num blocks; i++) blocks[i].move();
repaint();
}
45
}
}).start();
public void paint(Graphics g){
50
g.clearRect(0, 0, d.width, d.height);
for(int i = 0; i < num blocks; i++) blocks[i].draw(g);
}
}
2
7.4 練習問題
69
例題 7.3.3 次の 10 種類のブロックが壁に反射しながら水平方向に等速直線運動するプログラムを作成せ
よ.
幅
高さ
初期位置 (x, y)
x 方向の初速度
ブロック 1
5
10
(0, 0)
1
ブロック 2
10
10
(20, 30)
2
ブロック 3
15
10
(40, 60)
3
ブロック 4
20
10
(60, 90)
4
ブロック 5
25
10
(80, 120)
5
ブロック 6
5
10
(0, 150)
-1
ブロック 7
10
10
(20, 180)
-2
ブロック 8
15
10
(40, 210)
-3
ブロック 9
20
10
(60, 240)
-4
ブロック 10
25
10
(80, 270)
-5
2
7.4 練習問題
問題 7.1 Block クラスクラスを作成して,図 7.2 に示すようなブロックを描画せよ.配列も使うこと.
図 7.2
ブロックの配置
問題 7.2 前述のプログラムで,奇数行目のブロックが右方向,偶数行目のブロックが左方向に移動し,一方の壁から
ブロックが消えたら,他方の壁から現れるようなアニメーションを行うように変更せよ.
問題 7.3 3 個のボールが描画領域内で壁に反射しながら 2 次元平面内で等速直線運動するプログラムをクラスを用い
て作成せよ.ただし,個々のボールの初期位置,速度は乱数で発生させ,ボールどうしの衝突は考慮しなくても良い.
問題 7.4 前述の問題のボールの数を 10 個に増やし,個々に異った色で表示せよ.ただし,色は乱数で与えて良い.