LoginSignup
3
4

More than 3 years have passed since last update.

【Java】this と super 復習(エラー・コーディング規則)

Last updated at Posted at 2020-11-25

this と super の復習

this と super の使い方を知る

基本的には thissuper を使用する際の軸となる考え方は一緒で、主に下記のような場合に使用します。

クラスインスタンスフィールド変数メソッド の前に付与して

this と super の挙動

こちらのコードをもとに、見ていきます。

【GroupWorker.java】

package practiceThisAndSuper;

//「団員」という概念のクラス
public class GroupWorker {
    /** empId: 団員番号 */
    private int groupId;

    /** name: 氏名 */
    private String name;

    /** コンストラクタ: 引数なし */
    public GroupWorker() {
    }

    public GroupWorker(int groupId, String name) {
        this.groupId = groupId;
        this.name = name;
    }

    // ... フィールド変数のゲッターとセッター(※省きます)

    // Getter Setter -------------------------------

    // Getter
    public int getGroupId() {
        return this.groupId;
    }

    // Setter
    public void setGroupId(int groupId) {
        this.groupId = groupId;
    }

    // Getter
    public String getName() {
        return this.name;
    }

    // Setter
    public void setName(String name) {
        this.name = name;
    }

    // Getter Setter -------------------------------
    /**
     * 団員情報をコンソールへ出力するメソッド
     */
    public void printGroupWorker() {
        System.out.println("団員番号: " + this.groupId);
        System.out.println("氏名  : " + this.name);
    }
}


【RocketGroupWorker.java】

package practiceThisAndSuper;

//ロケット団員クラス
class RocketGroupWorker extends GroupWorker {
    /** isExecutive: 幹部フラグ(幹部であるかどうかを true/false で判断するためのフィールド変数) */
    boolean isExecutive;

    public RocketGroupWorker() {
    }

    /**
     * コンストラクタ: 引数あり
     *
     * @param groupId
     * @param name
     */
    public RocketGroupWorker(int groupId, String name) {
        // ①挙動確認: 親クラス(GroupWorker)のコンストラクタを呼び出す
        super(groupId, name);

        // ③挙動確認: thisを指定せずに格納する その1
        boolean isExecutive;
        if (groupId != 10) {
            isExecutive = true;
        } else {
            isExecutive = false;
        }
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");
    }

    /**
     * コンストラクタ: 引数あり
     *
     * @param groupId
     * @param name
     * @param isExecutive
     */
    public RocketGroupWorker(int groupId, String name, boolean isExecutive) {

        // ①挙動確認: 親クラス(GroupWorker)のコンストラクタを呼び出す
        super(groupId, name);

        // ②挙動確認: 自クラスのフィールド変数(isExecutive)へコンストラクタの引数を格納する
        this.isExecutive = isExecutive;

        // ③挙動確認: thisを指定せずに格納する その2
        isExecutive = true;
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");

        // ④挙動確認: thisとsuperで同じメソッドを呼び出す
        this.printGroupWorker();
        super.printGroupWorker();
    }

    // ... フィールド変数のゲッターとセッター(※省きます)
    // Getter
    public boolean getIsExecutive() {
        return this.isExecutive;
    }

    // Setter
    public void setIsExecutive(boolean isExecutive) {
        this.isExecutive = isExecutive;
    }
}

①挙動確認: 親クラス(GroupWorker)のコンストラクタを呼び出す

//①挙動確認: 親クラス(GroupWorker)のコンストラクタを呼び出す
super(groupId, name);

ポピュラーの使用方法で、「特に異なる 初期化処理 などを行う必要がない場合」はこれ。

super の実体は、「GroupWorker.java(親クラス)」
「RocketGroupWorker.java」 の コンストラクタ(二箇所)で呼び出していますが、
継承先で オーバーライド せずに親クラスコンストラクタ を利用しています。

②挙動確認: 自クラスのフィールド変数(isExecutive)へコンストラクタの引数を格納する

// ②挙動確認: 自クラスのフィールド変数(isExecutive)へコンストラクタの引数を格納する
this.isExecutive = isExecutive;

this の基本の使い方「ローカル変数とフィールド変数を明確に区別するため」。

例では、フィールド 変数の isExecutive と 引数の isExecutive を明確に区別した使用法で、this を付与して正しい値の受け渡しを行えています。

③挙動確認: thisを指定せずに格納する

自クラスの フィールド変数 なのか、ローカル変数/引数なのかが分かりづらい

this を付与していないことが原因で ローカル変数/引数の値を変更してしまっています。

この場合、起こりうる結果としては以下になります。

【その1の場合】
フィールド変数へ値を格納することを意図していたとしても、実際に値が格納されるのはローカル変数

        // ③挙動確認: thisを指定せずに格納する その1
        boolean isExecutive;
        if (groupId != 10) {
            isExecutive = true;
        } else {
            isExecutive = false;
        }
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");

【その2の場合】

  • フィールド変数へ値を格納することを意図していたとしても、実際に値が格納されるのは引数
  • 仮に②と③の処理順が逆であった場合でも、実際に値が格納されるのは引数になる
// ③挙動確認: thisを指定せずに格納する その1
        boolean isExecutive;
        if (groupId <= 10) {
            isExecutive = true;
        } else {
            isExecutive = false;
        }
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");
                 .....
                 .....
                 .....
                 .....

// ③挙動確認: thisを指定せずに格納する その2
        isExecutive = true;
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");

何故かというとコンパイル時には以下のような「優先順位が存在するから」です。

「ローカル変数/メソッドの引数」 > 「フィールド変数」

ローカル変数と メソッド引数 が同名の場合 は、コンパイル時に ワーニングが 表示されます ので即時対応が可能なのですが、
フィールド変数 とローカル変数(or メソッド引数)の場合 は、 ワーニングは 表示されません。

// mainメソッドで 定義
RocketGroupWorker be = new RocketGroupWorker(1000, "ムサシ");


// ③挙動確認: thisを指定せずに格納する その1
        boolean isExecutive;
        if (groupId != 10) {
            isExecutive = true;
        } else {
            isExecutive = false;
        }
        System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");

出力結果

幹部ではない

boolean 型の値に関しては、初期値が false となってます。

そのため、この場合は フィールド変数( isExecutive )の値と、 条件分岐後の格納値は一致しているため、
初期化時に期待する結果(フィールド変数の isExecutive が false であること)は変わりません。

isExecutive be = new isExecutive(1, "サカキ");のように コンストラクタ を呼び出した場合は、
条件分岐の結果true が格納されてしまいますので、フィールド変数( isExecutive )は意図した初期化ができません。

上記はフィールド変数に値を格納したいはずなので、

  • this を付与をする
  • 同名のローカル変数は避ける

といったことをやるべきです。

「一時的に同名のローカル変数」を用意したい場合

2パターン挙げます

1. 頭文字に「一時的な」という意味の「Temporary」を使用する

boolean tmpIsExecutive;
        if (groupId != 10) {
            tmpIsExecutive = true;
        } else {
            isExecutive = false;
        }
this.isExecutive = tmpIsExecutive;

2. 一番スッキリ書く方法

一番ベストの書き方はこちら
こちらの書き方がベストですが、最初のうちは 1の if と elseのブロックの考え方を意識すること。

// (groupId != 10)の結果値である true/false が戻り値として格納される
this.isExecutive  = (groupId != 10);

実行結果

...
isExecutive = true;
System.out.println(this.isExecutive ? "幹部です" : "幹部ではない");

出力結果

幹部ではない

この処理において isExecutive = true; が、
this.isExecutive = true; を意図してコーディングしたものである場合は話が変わってきます。

④挙動確認: thisとsuperで同じメソッドを呼び出す

こちらの

  • this.printGroupWorker();
  • super.printGroupWorker();

両方共、親 クラス である GroupWorker の printGroupWorker() を呼び出しています。
言い方を変えればオーバーライドすることにより、上記の thissuper の呼び出し先は変わるということ。

        // ④挙動確認: thisとsuperで同じメソッドを呼び出す
        this.printGroupWorker();
        super.printGroupWorker();

RocketGroupWorker クラスで printGroupWorker() をオーバーライドする

/**
 * 団員情報をコンソールへ出力するメソッド(幹部フラグを含む)
 */
public void printGroupWorker() {
    super.printGroupWorker();
    System.out.println("幹部フラグ: " + this.isExecutive);
}

上記のようにオーバーライドした場合、 **「this は自クラスオーバーライドした printGroupWorker() を参照する。」**

命名規則に関して重要なポイント

「値を受け渡す元と先の 変数に関して、意味的にも値として同じ場合、同様の命名規則」にしてください。

実装時はそのクラスの内容に適した 変数名・メソッドやメソッド名を実装していく訳ですが、要するに「疑問を招くような命名をするな」ということ。

例:「 name 」という変数に「 namae 」という[引数]が格納される

ですが、以下のように変数名と引数名がことなっていても、「同じ値がはいっていますよ」と明示したような引数名なら許容度です。

// parm は parameter(パラメーター)の意味
this.age = parmAge;
this.name = parmName;

ただ結論、this を付与する方向でコーディングしたほうが良い」です。

thisをつけることによって、コード量が大量になって、似たような命名の変数やメソッドを使用するなった場合であっても、目当てのコードが探しやすくなります。

 thisやsuperが許容されないケース

【ケース1】コンストラクタの前に処理が存在する

コンストラクタ内の処理として thissuper を使用する場合は、処理の一番時最初に記述しなければなりません。

    public BlueEmployee(int empId, String name) {
        boolean isExecutive = (groupId == 1);
                this(groupId, name); // ここでエラー
        super(groupId, name); // ここでエラー

この場合、以下のようなエラーが表示されます。

コンストラクター呼び出しは、コンストラクター内の最初のステートメントである必要があります。

このような場合はエラーが吐かれて実行は不可ですので、上記のようなエラーの時はチェックしてみましょう。

【ケース2】static修飾子ついている

static修飾子 が付与されたもの(クラス,変数、定数、メソッド) にはthissuper は付与することができません

サンプルコード

問題ないコードはこちら

public class StaticError {

    public static void main(String[] args) {
        StaticError.callPrintln(); // OK
    }

    private static void callPrintln() {
        System.out.println("解消方法1: 先頭にクラス名を付与する");
        System.out.println("解消方法2: メソッドの定義時にstaticを追加");
    }
}

実行結果

解消方法1: 先頭にクラス名を付与する
解消方法2: メソッドの定義時にstaticを追加

こちらはエラーになるコードです

public class StaticError {

    public static void main(String[] args) {
        this.callPrintln(); // staticメソッド内のためエラーになる
    }

    private static void callPrintln() {
        System.out.println("解消方法1: 先頭にクラス名を付与する");
        System.out.println("解消方法2: メソッドの定義時にstaticを追加");
    }
}

実行結果

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    static コンテキストでは this を使用できません

    at practiceThisAndSuper.StaticError.main(StaticError.java:6)

コード内に解消方法なども書きましたが、

static コンテキストでは this を使用できません

こちらのエラー(ワーニング)が出た際の解消方法は2つあります。

つまりは、staticとなったクラス、変数、定数、メソッドthis ではなく、自クラス名を明記しましょう

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4