Javaのオブジェクト指向備忘録(2)

前回の続きから。

@Contents

カプセル化 -> アクセス制御

カプセル化とは誰もがデータに触れないように隠しておくこと。他のクラスからデータやメソッドを変更できると、想定外に発生する不具合などが発生することがあるのでそれを防ぐためなどに行う。

アクセス修飾子

例:アクセス修飾子のおさらい↓

public class Modifier{ //クラスに対する修飾子

    private String field1; //プロパティに対する修飾子
    protected int field2; //プロパティに対する修飾子

    public void method(){ //メソッドに対する修飾子
        //何か処理を実装する
    }
}
publicすべてのクラスからアクセス可能
protected同じパッケージ内のクラスとサブクラスからアクセス可能
指定なし同じパッケージ内のクラスからアクセス可能
(パッケージプライベート)
private同じクラス内からのみアクセス可能
(外からアクセスできない)

上にあるものほど制限が緩く、下に行くほど厳しい。クラスに指定できる修飾子はpublicと指定なしだけだが、プロパティやメソッドには全ての修飾子が指定できる。

セッター / ゲッター -> 値の書き換え / 読み取り

package test;

class Student {
    private String studentName;
    private String clubName;
    private int vitality;

    Student() {
    }

    // 引数ありコンストラクタ
    Student(String studentName, String clubName, int vitality) {
        System.out.println("Constructor with arguments was called at initialization.");
        this.studentName = studentName;
        this.clubName = clubName;
        this.vitality = vitality;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public String getClubName() {
        return clubName;
    }

    public void setClubName(String clubName) {
        this.clubName = clubName;
    }

    public int getVitality() {
        return vitality;
    }

    public void setVitality(int vitality) {
        this.vitality = vitality;
    }
}
  • Getterメソッドの名前:get+プロパティ名
    • boolean型の場合のみ:is+プロパティ名
  • Setterメソッドの名前:set+プロパティ名

例:インスタンスをそのまま実行↓

package test;

public class SetterGetterSample {
    public static void main(String[] args) {

        Student stu = new Student();
        stu.studentName = "Kiki";
    }
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	フィールド Student.studentName は不可視です

	at test.SetterGetterSample.main(SetterGetterSample.java:8)

Student.javaprivateは自分のクラスでしか操作できないので、別のクラスであるSetterGetterSampleからは操作できない。なので、下記の操作を行う。

package test;

public class SetterGetterSample {
    public static void main(String[] args) {

        Student stu = new Student();
        stu.setStudentName("Kiki"); // Setter
        String sname = stu.getStudentName(); // Getter
        System.out.println(sname); // getterで取り出したものを画面出力
    }
}
Kiki

データだけ持たせるクラス(Student)と、操作設定するクラス(SetterGetterSample)は完全に別にする。

static -> newを不要に

  • メインメソッドにはpublic staticという2つの修飾子が付属している
    • publicはアクセス修飾子の1つ
  • staticは固定のもの(プロパティ、メソッド)を作成したい場合に活用する
    • new(初期化)しなくても呼べる
  • 書き方:クラス名.プロパティ名またはクラス名.メソッド名(引数)

継承 -> 差分だけ記述

package test;

public class Student {

    class StudentBase {
        public int studentId;
        public String studentName;
        public String clubName;
        public int vitality;

        StudentBase() {
        }

        StudentBase(int studentId, String studentName, String clubName,
                int vitality) {
            this.studentId = studentId;
            this.studentName = studentName;
            this.clubName = clubName;
            this.vitality = vitality;
        }

        void introduce() {
            vitality -= 10;
            System.out.println("My name is " + studentName + ".");
            System.out.println("I belong to the " + clubName + "club.");
        }

        void greeting() {
            vitality -= 10;
            System.out.println("Good morning!");
        }

        void greeting(String clubName) {
            vitality -= 10;
            System.out.println("Good morning everyone at " + clubName + "!");
        }

        void showVitality() {
            vitality -= 10;
            System.out.println("I have " + vitality + "health points left.");
        }
    }

    class StudentActivity extends StudentBase {
        public String homework;

        StudentActivity() {
        }

        StudentActivity(int studentId, String studentName, int vitality) {
            super.studentId = studentId;
            super.studentName = studentName;
            super.clubName = "homecoming";
            super.vitality = vitality;
            this.homework = "undecided";
        }

        void report() {
            vitality -= 10;
            System.out.println("I'll do my best today too!");
        }
    }
}

すべての生徒に共通するプロパティは、名前、クラブ名なのでそれらは親クラスに実装する。そして、部活動ごとに活動内容は異なるのでそれらは子クラスに実装する。そうすることにより、子クラスに自己紹介を記述しなくても親クラスから継承するだけでいい(差分だけ子クラスに記述)。

  • StudentBase:親クラス
  • StudentActivity:子クラス
  • extends StudentBaseと記述することで継承の仕組みを利用できる
  • super:親クラスのプロパティを明示する
  • this:自分のクラスのプロパティを明示して操作できる(子クラス)
  • メソッドやプロパティ -> 継承される
  • privateなプロパティとメソッド ->継承されない
  • コンストラクタ -> 継承されない

継承はいくらでも可能であるが、し過ぎると相互に依存関係が発生して修正しにくくなるので注意。

ポリモーフィズム -> 多態性

  • ポリモーフィズム
    • ある処理を命令したらクラスが違っても同じように振る舞ってくれる仕組み
      • オーバーライド(override
      • オーバーロード(overload

オーバーライド -> メソッドを再定義

package test;

class StudentBase {
    public int studentId;
    public String studentName;
    public String clubName;
    public int vitality;

    StudentBase() {
    }

    StudentBase(int studentId, String studentName, String clubName, int vitality) {
        this.studentId = studentId;
        this.studentName = studentName;
        this.clubName = clubName;
        this.vitality = vitality;
    }

    void introduce() {
        vitality -= 10;
        System.out.println("私は" + studentName + "だよ。");
        System.out.println(clubName + "で活動してる。");
    }

    void greeting() {
        vitality -= 10;
        System.out.println("おはよう!");
    }

    void greeting(String clubName) {
        vitality -= 10;
        System.out.println(clubName + "のみんな、おはよう!");
    }

    void showVitality() {
        vitality -= 10;
        System.out.println("残り体力は" + vitality);
    }
}

class StudentActivity extends StudentBase {
    public String homework;

    StudentActivity() {
    }

    StudentActivity(int studentId, String studentName, int vitality) {
        super.studentId = studentId;
        super.studentName = studentName;
        super.clubName = "パソコン部";
        super.vitality = vitality;
        this.homework = "未完";
    }

    void report() {
        vitality -= 10;
        System.out.println("今日はJavaについて学習しました。");
    }

    @Override
    void introduce() {
        vitality -= 10;
        System.out.println("私の名前は" + studentName + "と申します。");
        System.out.println(clubName + "に所属しています。");
        System.out.println("今日の課題は" + homework + "です。");
    }

    @Override
    void greeting() {
        vitality -= 10;
        System.out.println(super.clubName + "の皆さん" + "おはようございます");
    }
}

パソコン部としての子クラスを作成。親クラスではタメ口の自己紹介だが、子クラス(パソコン部)では新入部員という設定で敬語の自己紹介にしてみた。所属する部活動を引数にせずとも、子クラスで自動的に内容に応じた挨拶ができる。

  • オーバーライド -> 同じ名前で引数の違うメソッドを定義する
  • 親クラスにあったintroduce()をオーバーライドに。
    • 親クラスにあるメソッドと同じシグネチャ(引数が同じ)で再定義
  • greeting()メソッドも部活動によって挨拶が異なるのでオーバーライド化
package test;

class Student02 {
    public static void main(String[] args) {

        StudentActivity sa = new StudentActivity(10, "きき", 190);

        sa.homework = "ポートフォリオ作成";
        sa.introduce();
        sa.greeting();
        sa.report();
        sa.showVitality();
    }
}
私の名前はききと申します。
パソコン部に所属しています。
今日の課題はポートフォリオ作成です。
パソコン部の皆さんおはようございます
今日はJavaについて学習しました。
残り体力は150

呼び出し元を実行すると上記のようになる。親クラスに全ての機能を実装するのではなく、子クラスに特有の機能があるのであればオーバーライドで分けたほうが違いが明確になるので便利。

@Overrideはアノテーションと呼ばれるもので付けなくても良いが、付ければ他人から見た時に分かりやすいし、IDEがシグネチャの記述ミスを教えてくれる。

オーバーロード -> 同名のメソッドを定義

  • オーバーロード
    • 同じ名前のメソッドを定義できる仕組み
    • 引数の数や型は変えることが前提

オーバーライドについて書いたコードのgreeting()メソッドにオーバーロードを記述している。コンストラクタやメソッドに実際の文字列に限らず変数ごと渡すことができる。オーバーロードと言うとややこしいが、ただ単に複数の同名メソッドを作成するだけ(上書き)。

void greeting() {
    vitality -= 10;
    System.out.println("Good morning!");
}

void greeting(String clubName) {
    vitality -= 10;
    System.out.println("Good morning everyone at " + clubName + "!");
}
  • 引数の数を同じにしない
  • 引数が同じ個数でも型(Stringint)が違うのはOK
  • 同じ型しか使っていないけど、個数が違うのもOK

抽象クラスと抽象メソッド -> 実装を強制

package object;

public abstract class AbstractStudent {

    public String studentName;

    public abstract void greeting();
}
Good morning everyone at the PC club!
Good morning everyone at the English club!
  • 抽象メソッドはインスタンスを作成できない
    • 宣言のみで処理の中身は記述しない
  • AbstractStudentが受け手側の変数になる

抽象メソッドを使用すると実装されていない場合に警告が出る。複数人で作業する場合などはメソッドの作成し忘れや、オーバーライドミスが発生しやすいので利用すべき。

インターフェイス -> 共通する仕様を定義

  • 抽象メソッドのみを持つクラス
    • 実際の処理は記述しない
    • クラスの使い方や書式のみを定義
  • implementsを使う
    • 継承ではなく実装という
  • インターフェイスを実装したら必ず実行内容を定義
package object;

public interface InterfaceStudent {
    public abstract void greeting();
}
Good morning everyone at the PC club!
Good morning everyone at the PC club!
This is the Polymorphism!
Good morning everyone at the PC club!
This is the Polymorphism!
Good morning everyone at the PC club!
  • 抽象クラス、メソッドとほぼ同じ
  • newGreeting メソッドの引数にInterfaceStudentを使用
    • ポリモーフィズム
      • 代入されているインスタンスの型によって処理結果が変わる
  • 呼び出し側のコード量が減る、重複がなくなる
  • 維持保守管理がしやすくなる
    • 大規模システム

抽象クラスとインターフェイスの違い↓

  • 抽象クラス -> 具体的な処理内容を記述せず、メソッド名や引数などの定義だけを宣言する
    • 他のクラスの処理の骨組みを定義
    • 継承関係にある処理の再利用
    • 多重継承できない
    • 「 …である( is-a ) 」の関係
  • インターフェース -> クラスに含まれるメソッドの具体的な処理内容を記述せず、変数とメソッドの型のみを定義する
    • クラスで共通する仕様を定義
    • 多重継承できる
    • 「 …を持っている( has-a ) 」の関係

To comment

@Contents
閉じる