#author("2019-11-14T14:47:51+09:00","ocha","ocha") #author("2019-11-14T14:59:57+09:00","ocha","ocha") [[Lecture]] *Java プログラミング入門 [#a32ff810] このページは、学部2年生向け授業である、「マルチメディアプログラミング実習」 のために用意しました。 (Wikiの仕様で大文字小文字が混在した英単語に疑問符?が追加されるところがありますが、無視してください。) **第6章「クラスの基礎」 [#ncf7b7be] ***クラスとインスタンスの説明をします。よく聞いてください。 [#tcd34c08] まとめです: -クラスは型、インスタンスはそれから作られたもの --クラスがたいやきの焼き型だとすると、インスタンスはそれから作られたたいやき --クラスは設計図でインスタンスはそれから作られたもの -クラスとインスタンスそれぞれにメソッドと変数がある --クラス変数 例:何個のインスタンスを作ったか? --クラスメソッド 例:インスタンスを作れ --インスタンス変数 例:ひとつのたいやきの重さ --インスタンスメソッド 例:たいやきをxxグラム食べる http://gyazo.com/bbdd3587977a172f7fa7f00c4f5787e0.png ***すごく簡単なクラスの例 [#vbf621fd] 整数型のインスタンス変数を2個だけ持つクラスです。C言語の構造体に似ています。 public class Point { int x, y; } - 演習1 3次元の座標x,y,zを表現するクラスPoint3Dを定義してください。ファイル名はPoint3D.javaです。 x,y,zはそれぞれint型とします。定義したらコンパイルしてください。 先のPoint型のクラスにインスタンスメソッドを1個追加します。座標を表示するメソッドです。 public class Point { int x, y; void print() { System.out.println(x + ", " + y); } } - 演習2 先のクラスPoint3Dのインスタンスの持つ座標x,y,zが10,20,30だった場合、その座標を10, 20, 30のように表示するインスタンスメソッドprint()を定義してください。定義したらコンパイルしてください。 ***インスタンスの生成 [#cc06a50c] クラスからインスタンスを作ることをインスタンス化またはインスタンシエーションと言います。 先の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が表示されます。 - 演習3 TestPoint3Dというクラスを作って、その中のmain関数で、 先に作ったPoint3Dクラスからインスタンスを2個作り、それぞれの座標を10,20,30と-10,-20,-30に設定し、それぞれの内容をprint()メソッドを呼び出して表示するプログラムを作ってください。 以下のような実行結果が出るようにしてください http://gyazo.com/deeb78a70a724a01ce76945235d42ab8.png ***クラス変数・メソッドとインスタンス変数・メソッド [#h86c1fa6] 変数とメソッドの定義に使われる修飾子や戻り値の表現は、C言語に似ています。 - 戻り値がなければvoidと書きます - 戻り値があればその型を書きます。 以下はとても重要です。 - staticとついているのがクラス変数、クラスメソッドです。クラス名にピリオドをつけて呼び出します。 - staticが付いていないのがインスタンス変数、インスタンスメソッドです。インスタンス名にピリオドをつけて呼び出します。 (インスタンスを作らないと呼び出せません) クラス変数にはクラス名を指定してピリオドでアクセスします。例えば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()ではアクセスできません。インスタンスメソッドだからです。 そのほかの修飾子は、あとで説明しますが、アクセス制御だけ述べておきます。 - publicはすべてのクラスからアクセス可能 - protectedはサブクラスからアクセス可能(後で説明します) - privateはそのクラスの中からのみアクセス可能 です。 ***自分自身のメンバーへのアクセスthis [#ra357870] クラス変数、クラスメソッド、インスタンス変数、インスタンスメソッドのことをメンバーと呼ぶこともあります。クラスやインスタンスを構成するメンバーという意味です。上記で説明したように、メンバーを指定するには、そのメンバーが属しているクラスまたはインスタンス(これらをまとめてオブジェクトとも呼びます)を書いて、そのあとにピリオドを書いて、メンバーを指定します。もう一度復習すると、クラスならば、 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 の説明 [#ba29c5fb] 修飾子の説明: - staticとついているのがクラス変数、クラスメソッドです - staticが付いていないのがインスタンス変数、インスタンスメソッドです - publicはすべてのクラスからアクセス可能 - protectedはサブクラスからアクセス可能(後で説明します) - privateはそのクラスの中からのみアクセス可能 などから、今までお馴染みの、 public static void main (String args[]) {} は、どいういう意味だったでしょうか?考えてみてください。 - これはクラスメソッドです(staticなので。これが一番重要なこと) - これは値を返しません(void) - これはどのクラスからでもアクセスできます(publicなので。) さらに説明すると、クラスメソッドmainは特別なメソッドで、javaコマンドが実行するメソッドです。なので、public static void main()があれば、javaコマンドはこれを実行します。つまり、 -javaコマンドは、引数のクラスのクラスメソッドmainを実行します --staticと付いているのがクラス変数、クラスメソッドの印です -今作ったPointクラスにもクラスメソッドmainを実装すれば自分自身をテストできます ***クラスの中に自分自身のインスタンスをテストするクラスメソッドmain()を作る [#v24b8d7a] 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コマンドで、テストすることができます。 やっていることは、少しややこしいので、しっかり確認してください。 クラスメソッドの中で、自分が定義した方法で、インスタンスを作っています。そしてそれを操作しています。 -演習4 クラスPoint3Dに自分自身をテストするクラスメソッドmainを作ってみよう http://gyazo.com/bdef2ab8e69f7f6e62273c2d67d7af2e.png 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(); } ***変数には直接アクセスしないのが賢い設計 [#udb9cbfb] 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(); } } -演習5 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(); } ***指定された座標との距離を返すメソッドdistance() [#sab2f953] 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()); } -演習6 Point3Dに、 - 他の座標との距離を返すメソッド double distance ( int ptx, int pty, int ptz ) と、 - 他のPoint3Dインスタンスとの距離を返すメソッド double distance ( Point3D p ) を実装して、 次の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()); } **第7章 クラスとオブジェクトの操作 [#k1b96e7b] *** 他の点との距離を返すメソッドの例を説明しました [#xfa5f246] 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という名前は同じでも、引数の違いで、異なる動作をさせることができます。 *** Osaifuクラスを作ってみよう [#i9df5c2e] -Osaifuクラスからはインスタンスがたくさん作られる --Osaifuクラスはお財布の設計図/工場、ここから実際のお財布(インスタンス)が複数作られる --工場出荷時の残金は0円だけど、その後、いろいろな値になるだろう(お金持ちのお財布には残金が多いだろうし、逆ならば少ないだろう) --ということで残金はインスタンス変数とすべきだろう -Osaifuクラスには次のメソッド、変数が必要だろう --残金を表すインスタンス変数 int okane --お金を入金するインスタンスメソッド void in(int x); --お金を出金するインスタンスメソッド int out(int x); 戻り値は実際に出金できた金額(残金が不足ならばあるだけしか出せない) --残金を印刷するインスタンスメソッドがあってもよいだろう void print(); &br; http://gyazo.com/bd6eaafd220dc7ff791288089f293b28.png -Osaifuクラスをテストするmain()メソッドでは次のことをやってください -- Osaifuインスタンスを一つ作ってそれをsaifu1という名前にする -- Osaifuインスタンスをもう一つ作ってそれをsaifu2という名前にする --saifu1に1000円入金する --saifu2に500円入金する --saifu1とsaifu2が持っている金額を印刷する --saifu1から200円出金してそれをsaifu2に入金する --saifu1とsaifu2が持っている金額を印刷する 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(); } http://gyazo.com/c618901458edc1ba338cab87d2810157.png -Osaifu.java 解答編 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(); } } --このプログラムでは残金がマイナスになってしまってもかまわない設計になってしまっています。実際には残金だけの金額しか出せないはずなので、outメソッドを改良して、残金以上の金額を要求されたら、残金分だけしか出さないように変更してみてください。 --このためには、例えばoutメソッドを以下のようにすれば良い public int out(int x) { if(x < okane ) { okane = okane -x; return x; } else { int nokori = okane; okane =0; return nokori; } } *** コンストラクタ [#y0f882f5] コンストラクタはインスタンスが生成される時に自動的に呼び出されるメソッドです。コンストラクタを利用してインスタンスを初期化することができます。コンストラクタは「クラス名と同じ名前で、戻り値のないメソッド」です。先の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の使い方が今までと違って特殊です。 *** Osaifuクラスにコンストラクタを追加してみよう [#a8a56d7e] -Osaifu() で残金0のインスタンスを作る -Osaifu(int x) で引数を残金の初期値としたインスタンスを作る ことができるコンストラクタを作りましょう。 これをmainから呼んで、上記のプログラムと同じことをするためには、 Osaifu saifu1 = new Osaifu(1000);//インスタンスを作る //最初の保持金額を1000円にする Osaifu saifu2 = new Osaifu(500);//インスタンスを作る //最初の保持金額を500円にする とすれば良いです。 ***コンストラクタ解答編 [#w62499a7] 以下のコンストラクタを作ることになります。 Osaifu() { okane = 0; } Osaifu(int x) { okane = x; } これは以下のように書いても良いです。 Osaifu() { this(0); } Osaifu(int x) { okane = x; } ***インスタンスの参照 [#mb6379b9] 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) ) のようにします。 ***ガーベージコレクションとファイナライザ [#w0dec2d2] C言語ではmallocでメモリー確保した後、これを使わなくなったところでfreeしてシステムに返す必要がありました。そうしないと、どんどんメモリーを使ってしまうことになる可能性があります。この現象をメモリーリーク(メモリー漏洩)と呼ぶこともあります。オブジェクト指向言語でインスタンスを作る場合もシステムからメモリーを確保しています。これをシステムに返却する作業が必要な言語と、不要な言語があります。返却不要な言語は、使われなくなったメモリー領域を時々チェックして、自動的に返却してくれます。このような作業を(使われなくなった)ゴミ(のような領域を)集めると呼びます。ガーエージコレクションです。Javaではガーベージコレクションを行なってくれますので、newで確保した領域を、プログラマが明示的に返却する必要はありません。 インスタンスが破棄されるときに、何か特別な処置をしてほしいことがあります。例えば、インスタンスの中で入力をオープンして、破棄されるときにはクローズしたいという場合です。その場合は、finalize()メソッドを用意しておきます。そうすると、ガーベージコレクション対象になったところで、finalize()メソッドを自動的に起動してくれます。このメソッドのことをファイナラーザーとも呼びます。 ***クラス変数の初期化 [#g403bd1b] 先に述べたように、インスタンス変数は、コンストラクタで初期化ができます。では、クラス変数はどうやって初期化するのでしょうか。 一つは、定義する場所で初期化できます。 public class TestStatic [ static int x = 10; y = 20; ここでxはクラス変数で、yはインスタンス変数です。どちらも定義するところで初期化しています。 複雑な初期化をしたい場合は、staticイニシャライザを使用します。 例えば、10個の要素からなる配列を用意して、それの内容を添字の自乗になるよう初期化するには以下のようにします。 public class TestStatic { static int[] array; static { array = new int[10]; for (int I = 0; I < array.length; I++) array[I] = I * I; } ***final変数 [#c013ec29] 変数の宣言にfinalをつけると、その変数の値が変更の対象にならなくなります。クラス変数にfinalをつければ、定数を宣言する用途に使えます。C言語で#define文で定数を定義するような使い方ができます。 final static double PI = 3.14159265; **第8章 継承 [#ka97b6ac] オブジェクト指向言語では、クラスを拡張するいくつかの方法が導入されています。 Javaなどで一般的な手法は、継承です。引き継ぐということです。 すでにあるクラスの機能を拡張して、新しいクラスを作ることを考えます。 拡張する部分は新しく作るのですが、拡張しない部分は、古いクラスのまま使いたいところです。 通常は、古いクラスをコピーして、機能拡張して新しいクラスを作ることが考えられます。 でも、古いクラスの方も、そのまま使われ続けて、改良され続けるとしたら、新しいクラスにコピーした部分には反映されないことになります。 そこで継承を使います。 例えば、たい焼きの焼き型で説明します。 とても気に入った焼き型があるけど、しっぽに模様がないのだけが不満というケースを考えてみます。 そこで、現在の焼き型を親として、継承する新しい焼き型を作ります。 継承したクラスで定義すると、それだけが差分として使われます。 なので、しっぽの模様だけ定義しておけば、あとは全て親の機能が使われます。 子クラスの中身は、極端な場合、空っぽでも良いです。定義されないメンバーは全て親のメンバーが使われます。 ということで、 別のクラスを継承することで、 差分だけを書いて機能を拡張していくことができます。 http://gyazo.com/2c8ef14d8be63b9fc18c58d79f2f3879.png *** Osaifuクラスを継承してOsaifuUSDクラスを作ってみよう [#fc83e4ee] -OsaifuUSDクラスはアメリカで買い物をするときに便利な、アメリカドルでお金を出し入れできる財布です。 -あたらしく追加するメソッドは以下です -- void inUSD(int x) xドルをお財布に入金します。このとき円に換算して残高に追加します -- int outUSD(int x) xドルをお財布から出金します。このとき円に換算した金額だけ残高が減ります。戻り値はドルにしましょう。 --1ドルは90円にしてください(今とはかけ離れた値ですが、サンプルの画像がそれで作ってあるので、すみません) //-スーパークラスのメソッドを上書きするメソッドは以下です //-- void print() お財布の残高を円とドルで表示します -OsaifuUSDクラスをテストするmain()メソッドでは次のことをやってください -- OsaifuUSDインスタンスを一つ作ってそれをsaifu1という名前にする -- OsaifuUSDインスタンスをもう一つ作ってそれをsaifu2という名前にする --saifu1に1000円入金する --saifu2に5ドル入金する --saifu1とsaifu2が持っている金額を印刷する --saifu1から2ドル出金してそれをsaifu2に入金する --saifu1とsaifu2が持っている金額を印刷する -動作例 http://gyazo.com/f51ea5895aaab32bb30b9a940d91515d.png -ヒント:一行目はこれ public class OsaifuUSD extends Osaifu { -ヒント:mainはこんな感じ 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円になっています。 多分現在の為替レートとはかけ離れています。 (スクリーンショットを撮りなおすのが面倒なので、古いレートのままになっています) 為替レートを変数として持たせるとしたら、どうしたら良いでしょうか? - インスタンス変数 - クラス変数 のどちらにするか、考えてみましょう。 [解答]インスタンスで共通の情報ですので、おそらくクラス変数で定義するのが良いと思います。 *** OsaifuUSDクラスの改造(1) [#g8c0b7a2] 入出金のメソッドの中で、親の入出金メソッドを活用してみましょう。 public class OsaifuUSD extends Osaifu { public int outUSD(int usd) { return ( out( use * 90) / 90 ); } public void inUSD(int usd) { in( usd * 90 ); } … } このプログラムの中のoutメソッドとinメソッドは、親のクラスで定義されたメソッドです。 outメソッドで残金がマイナスにならないように処理してあれば、そのまま仕組みを利用することができます。 親のメソッドを呼ぶことを明示的に書くために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 ); } … } ***オーバーライド [#ad8de445] サブクラスに、スーパークラスと同じ名前と引数のメソッドを書くこともできます。 これをオーバライドト言います。 一つのクラスの中で、名前が同じメソッドを定義することをオーバーロードという言葉でお話ししました。 似ていますが、オーバーライドは、引数のパターンも同じです。 スーパークラスのメソッドを置き換えるものです。 ここでは、スーパークラスのprint()メソッドをサブクラスでオーバーライドしてみます。 ***スーパークラスのメソッドの呼び出し [#d39dcf0b] オーバーライドすると、通常(thisが使われる場合)はサブクラスで定義されたメソッドが実行されます。 つまりサブクラスの定義の中で、 print(); this.print(); はどちらもサブクラスのprint()が呼び出されます。 でも、明示的にスーパークラスで定義された方のメソッドを実行することも可能です。 その場合は、メソッドを呼び出すときにsuperと追記します。以下のようにします。 super.print(); *** OsaifuUSDクラスの改造(2) [#sa44f577] OsaifuUSDにprint()メソッドを追加して、 円表示の次の行に ( xxx USD ) と残高をUSD表示するようにしてください。 円表示を行う部分はスーパークラスOsaifuのインスタンスメソッドprint()を利用することを考えてみてください。 http://gyazo.com/39a7b24a8c45e15ac55693592c2eb7cf.png -解答例 public void print() { super.print(); System.out.println("( " + (okane / 90) + " usd )" ); } ***実行時の型 [#h051144d] サブクラスもちゃんとしたクラスですので、インスタンスを作ったらそのクラス型の変数に代入します。 例えば、ParentというクラスからChildというサブクラスを作ったとします。通常は、 Child x = new Child(); として使います。ここで親のクラスの変数に代入することもできます。 Parent y = new Child(); しかし、親のインスタンスを、子の型の変数には代入できません。 Child z = new Parent(); //これはできない 仕組みを考えれば理由は明白です。サブクラスは、スーパークラスの機能を全部持っているので、インスタンスを親の型にキャストしても問題ありません。逆に、親の型をサブクラスの型に変換すると、拡張された部分のメンバーを呼び出されたときに、対応できません。 instanceofという演算子があります。上記の例では、 if ( x instanceof Child) {} if ( x instanceof Parent) {{} とするとifの中の判定はどちらもtrueです。あるクラスのインスタンスは、そのクラスのインスタンスでもあり、そのスーパークラスのインスタンスでもあるのです。 ***抽象クラス [#eece29ff] 抽象クラスは、インスタンスを作れないクラスです。 いくつかのクラスの上位概念をまとめる役割があります。 例えば、猫というクラスのインスタンスは作れても、動物というクラスのインスタンスは作れない、という世界があったとしたら、動物は抽象クラスです。 例えば、犬クラス、猫クラスというクラスがあって、それらの上位概念として動物クラスを作ったとします。 その世界では、犬や猫のインスタンスは作れても、動物のインスタンスは作れないという設定にするのが自然かもしれません。 としたら、動物クラスは抽象クラスになります。 定義のオプションにabstractと書きます。 abstract class Animal {} この授業で抽象クラスを作成することはないと思いますが、マニュアルを見て発見したクラスのインスタンスを作ろうとすると、「抽象クラスだからインスタンスは作れない」というエラーが出ることがあります。そのクラスのサブクラスを探して、それを使ってください。 Readerは抽象クラスです。 Readerは抽象クラスです。下のリンクからReaderを探してみてください。 https://docs.oracle.com/javase/jp/8/docs/api/index.html ***抽象メソッド [#v050fd26] 抽象クラスと同様に、特定のメソッドがabstractと定義されることもあるようです。稀なケースだと思います。この場合も、サブクラスでオーバーライドして定義する必要があります。 public abstract void xxxx(); ***finalクラス [#tf82758d] クラスの定義にfinalをつけることができます。これ以上継承して欲しくない場合につけます。 final class XXXX {} メソッドの定義につけることもできます。サブクラスでオーバーライドして欲しくない場合に使います。 public final void XXXX(){} ***インタフェース [#k5d21dd0] 抽象クラスに似ていますがクラスではないので、インスタンス化もサブクラス作成もできません。 抽象メソッドと定数だけが定義されています。 この授業でインタフェースを定義することはないと思いますが、たくさん利用します。 インタフェースには空のメソッドが定義されています。 そのメソッドを実装していますという宣言にimplementsという予約語を使います。 例えば、MovableとErasableというインタフェースが定義されていて、 クラスの定義で、これらを実装しているという宣言は、以下のように書きます。 class MyShape implements Movable, Erasable {} このクラスでは、Movable, Erasableで定義されているメソッドを全て実装していないといけません。 ややこしいですが、後の授業で、実例がたくさん出てきますので、そこでまた理解してください。 Mouse Listenerはインタフェースの例です。 Mouse Listenerはインタフェースの例です。下のリンクから探してみてください。 https://docs.oracle.com/javase/jp/8/docs/api/index.html ***小テスト練習 [#g4c60a6e] 次回またはその次くらいの授業で、小テストを行います。 以下の内容が理解できるように、整理しておいてください。 以下のプログラムの中で、 クラス、インスタンス、サブクラス、スーパクラス、クラスメソッド、インスタンスメソッドがどれであり、 インスタンス化、継承がどこで行われているのか確認してください。 (importで始まる一行目はまだ説明していない内容なので小テストでは扱いません。無視してください) import javax.swing.JFrame; public class SampleWindow extends JFrame { public static void main(String args[]) { SampleWindow w = new SampleWindow(); w.setVisible(true); } }