Java-23(ポリモーフィズム、抽象化)
●勉強
①要点
・ポリモフィズムは、異なるクラスのオブジェクトが似たような振る舞いを示すことを可能にし、コードの一貫性と柔軟性を保てる。
・抽象クラスには抽象メソッドがあり、ポリモフィズムは状況に応じて異なる振る舞いをする。
・抽象クラスとポリモフィズムの使用方法とメリットを理解することで、プログラミングにおける目的が明確になります。
②2種類の継承
これまでの継承では、例えばスーパークラスのPersonクラスにサブクラスのStudentクラス(学籍番号などを追加)などの機能追加を考えていた。
もう一つは、サブクラスをもとに、スーパークラスを考える方法である。
サブクラスの共通をまとめてスーパークラスを作る考え方である。
メンバ変数や定義と実装内容が全て同じ場合は、そのまま。
定義のみであれば、定義のところだけを、クラスの中に入れられる。
このようなものを、抽象メソッドという。
③抽象クラスとポリモフィズム
実装内容を持たないメソッドを抽象メソッドと呼び、修飾子のところに、abstractをつける。abstract(抽象的な)
抽象メソッドを持つクラスを抽象クラスとも呼ぶ。これも修飾子の所に、abstracrtをつける。インスタンス化は不可である。
④サンプルプログラム
public abstract class Club{
private String name; // 部活名
public Club(String name){ //コンストラクタ
this.name = name;
}
public void display(){ //displayメソッド
System.out.println("部活動:" + name);
}
public abstract void practice(); //practiceメソッド→メソッドの定義だけしている。
}
practiceは、メソッドの定義のみしているので、曖昧→抽象メソッドと呼ばれる。抽象メソッドなので、abstractというキーワードを入れる。
クラス定義の中にも、同様に入れること。
public class TandF extends Club{
public TandF(String name){ //TandFのコンストラクタを定義している。
super(name);
}
public void practice(){ //先ほどの抽象メソッドの中身を実装している。
System.out.println("ウォームアップ");
System.out.println("インターバル");
System.out.println("筋肉トレーニング");
}
}
public class Football extends Club{
public Football(String name){ //TandFのコンストラクタを定義している。
super(name);
}
public void practice(){ //先ほどの抽象メソッドの中身を実装している。オーバーライド(スーパークラスのメソッドをサブクラスにて再定義すること)している。
System.out.println("ドリブル練習");
System.out.println("シュート練習");
System.out.println("ミニゲーム");
}
}
public class Student6{
private String name; // 氏名
private Club club; // Clubオブジェクト
public Student6(String name,Club club){
this.name = name;
this.club = club;
}
public void display(){
System.out.println("名前:" + name);
club.display();
}
public void practice(){
club.practice();
}
}
public class StuSample6{ //実行用クラス
public static void main(String[] args){ //2つの部活動のオブジェクトを作っている。
TandF taf = new TandF("陸上競技部");
Football fb = new Football("サッカー部");
Student6 stu1 = new Student6("菅原",taf); //一人目の生徒
stu1.display();
stu1.practice();
Student6 stu2 = new Student6("桜井",fb); //サッカー部のオブジェクトをコンストラクタに渡している。
stu2.display();
stu2.practice();
}
}
studentクラスのメソッドは、clubのオブジェクトによって、それぞれ異なる処理をしていた。これをポリモフィズムという。多様態という。
メリットは、
プログラムの書き方が統一できるという点は良い。
これらのクラスを利用する側も助かる。呼び出す側の書き方を統一できる。
→プログラムを拡張していきやすいというメリットがある!
⑤インターフェース
クラスは、メンバ変数とメソッドを持つことができる。
インターフェースは、定数と抽象メソッドを持つことができる。
interface Englishable {
String LANG = "英語";
void displayEng();
}
インターフェースを実装(implements)したクラスでは、すべてのメソッドの処理を定義する。
class Student implements Englishable {
String name;
void displayEng(){
System.out.println()
}
インターフェースは、複数実装できる。
継承は、枝を張るイメージ。
実装は、色を塗るイメージ。
継承関係にないもの同士を、抽象クラスとインターフェースを使い分けることが必要になる。
⑥サンプルプログラム
public interface Englishable{ //
String LANGUAGE = "[英語]"; //定数(finalのついた定数と扱われる)
void displayEng(); //メソッド(abstractがついた抽象メソッドとして扱われる)
}
public class Student7 implements Englishable{ //EnglishableクラスのdisplayEng()の中身を実装しなければならない
private String name;
public Student7(String name){
this.name = name;
}
public void display(){
System.out.println("名前:" + name);
}
public void displayEng(){
System.out.println(Englishable.LANGUAGE);
System.out.println("Name:" + name);
}
}
public class Baseball implements Englishable{
public void display(){
System.out.println("キャッチボール");
System.out.println("シートノック");
System.out.println("バッティング");
}
public void displayEng(){
System.out.println(Englishable.LANGUAGE);
System.out.println("catch ball");
System.out.println("seat knock");
System.out.println("batting practice");
}
}
public class StuSample7{ //実行
public static void main(String[] args){
Student7 stu = new Student7("Mike");
stu.displayEng();
Baseball bb = new Baseball();
bb.displayEng();
}
}
メリット
継承関係に全くない別のクラス同士でも、インターフェースを実装すると、
メソッドを統一することができるということ。
⑦パッケージ
複数のクラスをまとめる仕組みのこと。まとめかたとしては、
・パッケージ化の宣言
package パッケージ名;
package pack;
public class Student{
...
}
このように書くと、実際のフォルダ構成も、パッケージの名前と同じフォルダが作成され、その中にクラスファイルを入れることになる。
注意するべきことは、クラスをパッケージすると、
クラス名も変わるということ。
→pack.Studentクラス(完全修飾名)
・パッケージクラスの利用
しかしながら、いちいちパッケージ名.クラス名を書くのは
importして、パッケージ名を省略できる。
import pack.Student;
class StuSample{
...
パッケージ宣言→import→classへ
参考:クラスライブラリ
java.langパッケージ:基本的なクラス(自動インポート)
java.io:入出力関連
java.net:ネットワーク関連
⑧サンプルプログラム
import pack.Student8;
public class StuSample8{
public static void main(String[] args){
Student8 stu = new Student8("菅原");
stu.display();
}
}
package pack;
public class Student8{
private String name;
public Student8(String name){
this.name = name;
}
public void display(){
System.out.println("名前:" + name);
}
}
import java.io.*;
public class WriteFile{
public static void main(String[] args){
try{
BufferedReader r = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter w = new PrintWriter(
new BufferedWriter(new FileWriter("output.txt")));
String str;
while((str = r.readLine()) != null){
w.println(str);
}
r.close();
w.close();
} catch(IOException e) {
System.out.println(e);
}
}
}
●復習
データには基本データ型と参照型の2種類がある。
小さなデータ型から大きなデータ型に変換する場合は自動的に型変換が行われるが、逆の場合はキャストが必要になる。
クラス間に継承関係がある場合には、型変換が自動的に行われます。
サブクラスからスーパークラスへの変換では問題が発生する可能性があり、その場合にはキャストが必要になる。
▶️参照型の型変換とは
型変換する前のクラスと、変換後のクラスの間で継承関係になるときに参照型の型変換ができる。
継承関係にあるクラス同士でオブジェクトの型変換は、
スーパー = サブの時、 自動で行われる。
サブ = スーパーの時、キャストで明示する。
▶️自動とキャストの違いとその理由
Student stu1 = new Student(...);
Person psn = stu1;
Studentクラス(入れ物の箱が大きい) > Personクラス(小さい)
ここで、stu1で扱える範囲は大きい。
逆に、psnで扱える範囲は小さい。
親クラス
Personクラス = ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️
子クラス
Studentクラス = ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️ (←ここまでを継承) + ▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️▫️ (←ここまでを追加)
よって、箱の大きいstu1を、範囲の小さいpsnに入れることは可能。
Student stu1 = new Student(...);
Person psn = stu1;
Student stu2 = (Student)psn;
psn.display();
psn.chgStuNo(・・・);
stu2.chgStuNo(・・・);
以上の中で、
Student stu2 = (Student)psn;
ここでいう、(Student)がキャストになります。
Studentクラスの入っている要素が多い理由は、Personクラスを継承したからである。つまり、要素の少ないPersonクラスがスーパークラス(親クラス)で、要素の多いStudentクラスが参照。
スーパークラスに、サブクラスに代入する時には、自動で行われる。
なぜなら、サブクラスの中の情報には必ずスーパークラスの情報があるから、型変換するには問題はない。
しかし、スーパークラスから、サブクラスに代入する時には、要素がたくさん入った大きな箱へと広げるため、メモリ上に必ずしも存在するとは限らないので、ただ変換するとコンパイルエラーが起きるので、型変換をする時には、キャストが必要である。ということになる。
▶️サンプルプログラム
public class Person5{
private String name;
public Person5(String name){
this.name = name;
}
public void display(){
System.out.println("名前:" + name);
}
}
public class Student5 extends Person5{
private int stuNo;
public Student5(String name, int stuNo){
super(name);
this.stuNo = stuNo;
}
public void display(){
super.display();
System.out.println("学籍番号:" + stuNo);
}
public void chgStuNo(int stuNo){
this.stuNo = stuNo;
}
}
public class StuSample5{
public static void main(String[] args){
Student5 stu1 = new Student5("菅原",1);
Person5 psn = stu1;
psn.display();
// psn.chgStuNo(1001);
/* 補足:instanceofキーワード
オブジェクトのクラスを特定する
対象オブジェクト instanceof クラス名 */
if(psn instanceof Student5){
Student5 stu2 = (Student5)psn;
stu2.chgStuNo(1001);
stu2.display();
}
}
}
▶️オーバーロードとオーバーライド
オーバーロード
・1つのクラスの中で同じ名前のメソッドを複数定義できること。
・クラスにない、同じ名前で引数の型や数が違うメソッドを定義すること。
同じような機能を持つメソッドであっても引数のデータ型が異なれば別々
のメソッドを用意する必要がある。
オーバーライド
・スーパークラスとサブクラスでのメソッドの上書きである。
・サブクラスで、スーパークラスのメソッドを再定義する。
条件は、戻り値の型、メソッド名、引数の型と数が全て同じ場合。
▶️コンストラクタ
オブジェクトの初期化のための特殊なメソッド。
名前がクラス名と同じであり、戻り値を持たないこと(voidを設定)
newクラス名(コンストラクタへの引数)があることが条件。
コンストラクタを定義しない→デフォルトコンストラクタが生成。
▶️static
全インスタンス変数が使える
メンバ変数やメソッドを定義する時には、staticを指定する。
クラス名.変数[メソッド]名と書きなおす必要がある。
▶️カプセル化
「アクセス修飾子」でクラス・メンバ変数・メソッドの公開範囲を指定
⦅公開範囲⦆
public
同クラス、同パッケージ、サブクラス、その他
protected
同クラス、同パッケージ、サブクラス
(なし)
同クラス、同パッケージ
private
同クラス
代入前処理ができる、修正範囲を変えやすいというメリットがある。
class Student{
private int score;
public void setScore(int s){
(中略)
score = s;
}
}
○ stu.setScore(80);
× stu.score = 80;
//このように、直接代入をすると、コンパイルエラーが出ることになる。
▶️継承
継承関係にある親クラスをスーパークラス、子クラスをサブクラスという。extendで継承可能。
▶️this とsuper
this とsuperによるメソッド・メソッドの呼び出し
this.~ = 自オブジェクトの〜
super.~ = スーパークラスの〜
プログラムの処理を再び書く必要がない。引数名を考えなくて良い。
public class Person3{
private String name;
public Person3(String name){
this.name = name;
}
public void display(){
System.out.println("名前:" + name);
}
}
this とsuperによるコンストラクタの呼び出し
this()~ = 自オブジェクトの〜
super()~ = スーパークラスの〜
これらは、コンストラクタ内の先頭に記述するようにする。
なければ、自動的にsuper();が設定されることになる。
少しずつですが、投稿をしております。 noteで誰かのためになる記事を書いています。 よろしくおねがいします。