このページは、学部2年生向け授業である、「マルチメディアプログラミング実習」 のために用意しました。
(Wikiの仕様で大文字小文字が混在した英単語に疑問符?が追加されるところがありますが、無視してください。)
まとめです:
整数型のインスタンス変数を2個だけ持つクラスです。C言語の構造体に似ています。
public class Point { int x, y; }
先のPoint型のクラスにインスタンスメソッドを1個追加します。座標を表示するメソッドです。
public class Point { int x, y; void print() { System.out.println(x + ", " + y); } }
クラスからインスタンスを作ることをインスタンス化またはインスタンシエーションと言います。
先のPointクラスとは別の、テスト用クラスTestPointを用意して、その中でPointインスタンスを作るには、以下のようにします。 Pointクラスは別のファイルで定義されていることになりますが、同じディレクトリにそのファイルがあれば、ファイル名を手掛かりに探してくれます。
public class TestPoint { public static void main(String args[]) { Point pt1 = new Point(); Point pt2 = new Point(); } }
配列の時と同じくnewコマンドが使われています。new Point()により、Pointクラスの設計図にしたがって、Point型インスタンスを格納するためのメモリー空間が確保されます。確保されたメモリー領域への参照をPoint型変数に代入しています。ここでは2つのインスタンスを作りました。同じ設計図(クラス)から作られましたが、それぞれ別のものです。
インスタンスのインスタンス変数、インスタンスメソッド(これらを合わせてメンバーと言うこともあります)にアクセスするためには、ピリオドを用います。C言語の構造体と同じです。
pt1とpt2のインスタンス変数に値を代入して、インスタンスメソッドprint()を呼び出すプログラムは以下のようになります。
public class TestPoint { public static void main(String args[]) { Point pt1 = new Point(); Point pt2 = new Point(); pt1.x = 10; pt1.y = 20; pt2.x = - pt1.x; pt2.y = - pt1.y; pt1.print(); pt2.print(); } }
この結果、10, 20と-10, -20が表示されます。
TestPoint3Dというクラスを作って、その中のmain関数で、 先に作ったPoint3Dクラスからインスタンスを2個作り、それぞれの座標を10,20,30と-10,-20,-30に設定し、それぞれの内容をprint()メソッドを呼び出して表示するプログラムを作ってください。 以下のような実行結果が出るようにしてください
変数とメソッドの定義に使われる修飾子や戻り値の表現は、C言語に似ています。
以下はとても重要です。
クラス変数にはクラス名を指定してピリオドでアクセスします。例えばjava言語には、Mathというクラスが用意されています。MathクラスにはPIというクラス変数が用意されています。なので、Math.PIでアクセスできます。
class MathTest { public static void main (String args[]) { System.out.println(Math.PI); } }
またMath.random()クラスメソッドで乱数が得られます。以前の演習で使いました。
インスタンス変数はインスタンスを作ってからアクセスします。上の例でPointクラスでprint()インスタンスメソッドを作りました。これは、
Point pt1 = new Point(); pt1.print();
としてアクセスできます。クラスメソッドではありませんので、Point.print()ではアクセスできません。インスタンスメソッドだからです。
そのほかの修飾子は、あとで説明しますが、アクセス制御だけ述べておきます。
です。
クラス変数、クラスメソッド、インスタンス変数、インスタンスメソッドのことをメンバーと呼ぶこともあります。クラスやインスタンスを構成するメンバーという意味です。上記で説明したように、メンバーを指定するには、そのメンバーが属しているクラスまたはインスタンス(これらをまとめてオブジェクトとも呼びます)を書いて、そのあとにピリオドを書いて、メンバーを指定します。もう一度復習すると、クラスならば、
Math.PI
でクラスMathのクラス変数のPIにアクセスできます(円周率が返ってきます)。
インスタンスならば、クラスからインスタンスを生成した後で、ピリオドでアクセスします。
Point pt1 = new Point(); pt1.print();
このようにメンバーにアクセスするには、そのメンバーが属しているオブジェクトを指定するのが正式なやり方です。ただし、同じオブジェクトの中のメンバーを指定する場合には、オブジェクトを略しても良いです。例えば、先のPointの例で、
public class Point { int x, y; void print() { System.out.println(x + ", " + y); } }
と、書いてあります。インスタンスメソッドのprint()の中で、xとyを指定しています。これは自分自身のインスタンス変数です。なのでピリオドで指定していません。省略できるからです。
これをインスタンスの名前を指定して書くことはできるでしょうか?よく考えるととても難しいです。まずは、これは設計図段階での記述なので、これから作られたインスタンスにどのような名前がつけられるかわかりません。また、特定の名前を想定すると、別の名前をつけられた時に参照できないかもしれません。このような場合でも、自分自身のメンバーを指定しているのだということを明示的に書く方法があります。それがthisです。上の例は、
public class Point { int x, y; void print() { System.out.println(this.x + ", " + this.y); } }
と書くこともできます。メソッドの中で一時的に宣言した変数ではなくて、メンバーにアクセスしているのだということがわかりやすいので、プログラムも読みやすくなります。thisは、積極的に使いましょう。
修飾子の説明:
などから、今までお馴染みの、
public static void main (String args[]) {}
は、どいういう意味だったでしょうか?考えてみてください。
さらに説明すると、クラスメソッドmainは特別なメソッドで、javaコマンドが実行するメソッドです。なので、public static void main()があれば、javaコマンドはこれを実行します。つまり、
public class Point { int x, y; void print () { System.out.println(this.x + ", " + this.y); } public static void main(String args[]) { Point pt1 = new Point(); //自分自身からインスタンスを作る Point pt2 = new Point(); //自分自身からもう一つインスタンスを作る pt1.x = 10; pt1.y = 20; pt2.x = - pt1.x; pt2.y = - pt1.y; pt1.print(); pt2.print(); } }
これで、java Pointコマンドで、テストすることができます。 やっていることは、少しややこしいので、しっかり確認してください。 クラスメソッドの中で、自分が定義した方法で、インスタンスを作っています。そしてそれを操作しています。
クラスPoint3Dに自分自身をテストするクラスメソッドmainを作ってみよう
public static void main(String args[]) { Point3D pt1 = new Point3D(); //自分自身からインスタンスを作る Point3D pt2 = new Point3D(); //自分自身からもう一つインスタンスを作る pt1.x = 10; pt1.y = 20; pt1.z = 30; pt2.x = - pt1.x; pt2.y = - pt1.y; pt2.z = - pt1.z; pt1.print(); pt2.print(); }
C言語の構造体のように変数にアクセスできます。 でも、オブジェクト指向の設計では、外部からは極力変数にアクセスさせない設計が良いと言われています。 変数にアクセスするには、アクセス用のメソッドを使うのが良いと言うことです。 これにより、 変数の型などを将来変更してもメソッドの書き換えで対応できるからです。 修飾子をprivateにすると、クラスの外からアクセスできなくなって、保護できます。 そして、アクセス用のメソッドを書いておきます。
例えば、Pointクラスでしたら以下のようにします。
public class Point { private int x, y; void set(int newx, int newy) {this.x=newx; this.y=newy;} int getx() { return this.x;} int gety() { return this.y;} void print () { System.out.println(this.x + ", " + this.y); } public static void main(String argv[]) { Point pt1= new Point(); Point pt2 = new Point(); pt1.set(10,20); pt2.set(-pt1.getx(), -pt1.gety()); pt1.print(); pt2.print(); } }
Point3Dのメソッドを充実させて次のmain()メソッドで
10, 20, 30 -10, -20, -30
という結果が出るようにしましょう
public static void main(String argv[]) { Point3D pt1 = new Point3D(); Point3D pt2 = new Point3D(); pt1.set(10, 20, 30);//インスタンス変数を設定 pt2.set(-pt1.getx(), -pt1.gety(), -pt1.getz()); pt1.print();//インスタンスメソッド呼出 pt2.print(); }
print()メソッドの他に、もう少し複雑な計算をするメソッドを考えてみましょう。特定の座標を指定した場合に、その座標との距離をdouble型で返すメソッドです。平方根を計算するMathクラスのクラスメソッドを使っています。
double distance ( int ptx, int pty ) { int dx = ptx - this.x; int dy = pty - this.y; return Math.sqrt(dx * dx + dy * dy ); }
今度は、座標ではなくて、他のPointインスタンスを引数にして、自分の点から、引数で与えられた点までの距離を返すメソッドと考えてみます。こんなメソッドです。
double distance ( Point p ) { int dx = p.getx() - this.x; int dy = p.gety() - this.y; return Math.sqrt(dx * dx + dy * dy ); }
この2つのメソッドは、どちらもPointクラスに実装することができます。よく見ると、先のメソッドと名前が同じです。C言語では名前が同じ関数を定義できませんでした。オブジェクト指向の言語では、引数の種類・数が違えば、同じ名前の関数を定義できます。引数を含めて関数を識別してくれます。このことを、多重定義、もしくはオーバーロードと呼びます。
違う関数ですので、片方のdistanceの中から、もう一方のdistanceを呼び出すこともできます。なので、以下のように定義することもできます。
double distance ( int ptx, int pty ) { int dx = ptx - this.x; int dy = pty - this.y; return Math.sqrt(dx * dx + dy * dy ); } double distance ( Point p ) { return this.distance(p.getx(), p.gety()); }
Point3Dに、
次のmain()メソッドで
10, 20, 30 -10, -20, -30 74.83314773547883
という結果が出るようにしましょう
public static void main(String argv[]) { Point3D pt1 = new Point3D(); Point3D pt2 = new Point3D(); pt1.set(10, 20, 30);//インスタンス変数を設定 pt2.set(-pt1.getx(), -pt1.gety(), -pt1.getz()); pt1.print();//インスタンスメソッド呼出 pt2.print(); System.out.println(pt1.distance(pt2)); }
double distance ( int ptx, int pty, int ptz ) { int dx = ptx - this.x; int dy = pty - this.y; int dz = ptz - this.z; return Math.sqrt(dx * dx + dy * dy + dz * dz); } double distance ( Point3D p ) { return this.distance(p.getx(), p.gety(), p.getz()); }
double distance ( int ptx, int pty ) { int dx = ptx - this.x; int dy = pty - this.y; return Math.sqrt(dx * dx + dy * dy); } double distance ( Point p ) { return this.distance(p.getx(), p.gety()); }
distanceという名前は同じでも、引数の違いで、異なる動作をさせることができます。
public static void main(String argv[]) { Osaifu saifu1 = new Osaifu(); Osaifu saifu2 = new Osaifu(); saifu1.in(1000); saifu2.in(500); saifu1.print(); saifu2.print(); saifu2.in(saifu1.out(200)); saifu1.print(); saifu2.print(); }
public class Osaifu { int okane; public void in (int x) { okane += x; } public int out(int x) { okane -= x; return x; } public void print() { System.out.println( "okane = " + okane +" yen"); } public static void main(String argv[]) { Osaifu saifu1 = new Osaifu(); Osaifu saifu2 = new Osaifu(); saifu1.in(1000); saifu2.in(500); saifu1.print(); saifu2.print(); saifu2.in(saifu1.out(200)); saifu1.print(); saifu2.print(); } }
public int out(int x) { if(x < okane ) { okane = okane -x; return x; } else { int nokori = okane; okane =0; return nokori; } }
コンストラクタはインスタンスが生成される時に自動的に呼び出されるメソッドです。コンストラクタを利用してインスタンスを初期化することができます。コンストラクタは「クラス名と同じ名前で、戻り値のないメソッド」です。先のPointクラスにコンストラクタを追加してみました。
public class Point{ int x, y; Point() { x = y = 0; } Point(int pix, int pty) { x = ptx; y= pty; } 省略 }
インスタンスを生成する際の演算子newのあとに指定するのは、実はコンストラクタだったのです。コンストラクタは、演算子newとの組み合わせでのみ使用されます。上記で指定したコンストラクタは、次のように使われます。
Point pt1 = new Point(); Point pt2 = new Point(10, 20);
これで、最初のコンストラクタと、2番目のコンストラクタが呼び出されます。コンストラクタはnewを使用する時にしか使えません。
コンストラクタから他のメソッドを呼び出すことも可能です。例えばx,yを設定するメソッドがすでにあれば以下のように指定できます。
Point(int pix, int pty) { set(ptx, pty); } void set(int ptx, int pty) { this.x = ptx; this.y = pty; }
コンストラクタの中でコンストラクタを呼び出すこともできます。
Point() { this(0, 0); }
これはコンストラクタPoint (x, y)を呼び出しています。thisの使い方が今までと違って特殊です。
ことができるコンストラクタを作りましょう。
これをmainから呼んで、上記のプログラムと同じことをするためには、
Osaifu saifu1 = new Osaifu(1000);//インスタンスを作る //最初の保持金額を1000円にする Osaifu saifu2 = new Osaifu(500);//インスタンスを作る //最初の保持金額を500円にする
とすれば良いです。
以下のコンストラクタを作ることになります。
Osaifu() { okane = 0; } Osaifu(int x) { okane = x; }
これは以下のように書いても良いです。
Osaifu() { this(0); } Osaifu(int x) { okane = x; }
Point pt1 = new Point(10,20); Point pt2 = pt1; Point pt3 = new Point(10,20);
この時、
if(pt1 == pt2)
という判断をするとこれはtrueになります。
しかし
if(pt1 == pt3)
という判断をすると結果はfalsです。
参照先が同じなのか、その中のインスタンス変数が同じなのかの違いです。インスタンス変数が同じかどうかは、その変数の値を直接調べて比較する必要があります。例えば、
if( ( pt1.x == pt3.x) && (pt1.y == pt3.y) )
のようにします。
継承の話をしました。 別のクラスを継承することで、 差分だけを書いて機能を拡張していくことができます。
public class OsaifuUSD extends Osaifu {
public static void main(String argv[]) { OsaifuUSD saifu1= new OsaifuUSD(); OsaifuUSD saifu2=new OsaifuUSD(); saifu1.in(1000); saifu2.inUSD(5); saifu1.print(); saifu2.print(); saifu2.inUSD(saifu1.outUSD(2)); saifu1.print(); saifu2.print(); }
public class OsaifuUSD extends Osaifu { public int outUSD(int usd) { okane-=usd * 90; return usd; } public void inUSD(int usd) { okane+=usd * 90; } public static void main(String argv[]) { OsaifuUSD saifu1 = new OsaifuUSD(); OsaifuUSD saifu2 = new OsaifuUSD(); saifu1.in(1000); saifu2.inUSD(5); saifu1.print(); saifu2.print(); saifu2.inUSD(saifu1.outUSD(2)); saifu1.print(); saifu2.print(); } }
大昔の教科書にあったサンプルを使っているので、為替レートが90円になっています。多分現在の為替レートとはかけ離れています。為替レートを変数として持たせるとしたら、どうしたら良いでしょうか?
のどちらにするか、考えてみましょう。
入出金のメソッドの中で、親の入出金メソッドを活用してみましょう。
public class OsaifuUSD extends Osaifu { public int outUSD(int usd) { return ( out( use * 90) / 90 ); } public void inUSD(int usd) { in( usd * 90 ); } … }
親のメソッドを呼ぶことを明示的に書くためにsuper.をつかってもよい。
public class OsaifuUSD extends Osaifu { public int outUSD(int usd) { return ( super.out( use * 90) / 90 ); } public void inUSD(int usd) { super.in( usd * 90 ); } … }
OsaifuUSDにprint()メソッドを追加して、 円表示の次の行に ( xxx USD ) と残高をUSD表示するようにしてください。 円表示を行う部分はスーパークラスOsaifuのインスタンスメソッドprint()を利用することを考えてみてください。
public void print() { super.print(); System.out.println("( " + (okane / 90) + " usd )" ); }
以下のプログラムの中で、 クラス、インスタンス、サブクラス、スーパクラス、クラスメソッド、インスタンスメソッドがどれであり、 インスタンス化、継承がどこで行われているのか確認してください。 (importで始まる一行目はまだ説明していない内容なので小テストでは扱いません。無視してください)
import javax.swing.JFrame; public class SampleWindow extends JFrame { public static void main(String args[]) { SampleWindow w = new SampleWindow(); w.setVisible(true); } }