Getter/Setterメソッド - 萌えJava超入門
第十六章 インスタンス

Getter/Setterメソッド

Getter/Setterは、アクセスメソッドの定型化が目的。
重要なのはあくまでもカプセル化

目次


1.Getter/Setterとは


変数を private修飾子でアクセスを禁止して、変数にアクセスするためのメソッドを作る。
これがカプセル化(狭義のカプセル化、データの隠蔽)とご紹介しました。
この、アクセスメソッドの書き方にちょっとした作法があります。
ご紹介しますね。

Getterとは
Getter(ゲッター)は、変数の値を取得するメソッド。

Setterとは
Setter(セッター)は、変数に値を代入するメソッド。

Getter/Setterの書式
  private データ型 変数名;

  /*Getter*/
  データ型 get変数名(){
      return 変数名;
  }

  /*Setter*/
  void set変数名(データ型 変数名){
      //処理
      this.変数名 = 変数名;
  }

こんな感じ。
Setterには、値の整合をとるための
処理を加える。
Getter/Setterの例
  private String name;

  String getName(){
      return name;
  }

  void setName(String name){
      //処理
      this.name = name;
  }

Getterは、変数の値を戻り値として、呼び出し元に返すだけです。

Setterには、代入する値が適切であるかを確認する処理や、
他の変数にも同時に変更を加えるといった、整合を守るたもの処理を加えます。



2.Getter/Setterのサンプルコード


Member_07クラスの変数は、「no」と「name」の二つです。
いずれも private修飾子で、インスタンス外から直接参照することはできません。

アクセスメソッドとして、それぞれ Getter/Setter を設定しました。
カプセル化ですね。

//Member_07.java
class Member_07{
    private int no;
    private String name;

    public int getNo(){
        return no;
    }
    public void setNo(int no){
        this.no = no;
    }

    public String getName(){
      return name;
    }
    public void setName(String name){
      this.name = name;
    }

    public Member_07(int no, String name){
        this.no = no;
        this.name = name;
    }
}

//Driver_07.java
class Driver_07{
    public static void main(String[] args){

      Member_07 shiz = new Member_07(1, "館石静乃");

      System.out.println(shiz.getName());
      shiz.setName("シズちゃん");
      System.out.println(shiz.getName());

    }
}

Getter/Setter を利用して、インスタンス外から変数にアクセスしています。

コマンドライン
ws>javac -encoding UTF-8 Driver_07.java
ws>java Driver_07
館石静乃
シズちゃん

Getter/Setter は
アクセスメソッドの定型化が
目的と思っていい。
定型化。

Member_07クラスは、カプセル化するために4つのアクセスメソッドを設定しました。
Getter/Setterは、変数名と同じ名前の「Getter 参照用メソッド」と
「Setter 編集用メソッド」という形で、
オブジェクトの使用者がわかりやすいように配慮したのです。


重要
  Getter/Setter は、カプセル化することを前提として
  アクセスメソッドを定型化します。



3.Getterの戻り値は変数ではない


以下のサンプルの様な書き方はできません。

//Driver_07NG.java
class Driver_07NG{
    public static void main(String[] args){

      Member_07 shiz = new Member_07(1, "館石静乃");
      System.out.println(shiz.getName());
      shiz.getName() = "シズちゃん"; //NG
    }
}

コマンドライン
ws>javac -encoding UTF-8 Driver_07NG.java
Driver_07NG.java:8: エラー: 予期しない型
      shiz.getName() = "シズちゃん";
                  ^
  期待値: 変数
  検出値:    値
エラー1個


一見、shiz.name の値に "シズちゃん" を代入できそうなのですが、
Getterの戻り値は、変数ではなく値なので、代入できません。

shiz.name が privateである限り、アクセス手段は Setterだけです。

重要
  Getterの戻り値は、変数ではなく値です。
  値には代入はできません。




4.Getter/Setterの良くない例


今度の Member_08クラスには、インスタンス変数が5つあります。
これらの変数をprivate修飾子で隠蔽して、Getter/Setter を設定しました。
一見カプセル化ができている様なのですが、実はできていません。

//Member_08.java
//おススメできないコードです
class Member_08{
    private int no;
    private String name;
    private int max_hp;
    private int now_hp;
    private boolean life = true;

    public int getNo(){
        return no;
    }
    public void setNo(int no){
        this.no = no;
    }

    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }

    public int getMax_hp(){
        return max_hp;
    }
    public void setMax_hp(int max_hp){
        max_hp = max_hp<1 ? 1 : max_hp;
        now_hp = now_hp>max_hp ? max_hp : now_hp;
        this.max_hp = max_hp;
    }

    public int getNow_hp(){
        return now_hp;
    }
    public void setNow_hp(int now_hp){
        now_hp = now_hp>max_hp ? max_hp : now_hp;
        if(now_hp<0){
            now_hp = 0;
            life = false;
        }
        this.now_hp = now_hp;
    }

    public boolean isLife(){
        return life;
    }
    public Member_08(int no, String name, int max_hp, int now_hp){
        this.no = no;
        this.name = name;
        this.max_hp = max_hp;
        this.now_hp = now_hp;

        max_hp = max_hp<1 ? 1 : max_hp;
        now_hp = now_hp>max_hp ? max_hp : now_hp;
        now_hp = now_hp<0 ? 0 : now_hp;
    }
}

//Driver_08.java
//おススメできないコードです
class Driver_08{
    public static void main(String[] args){

      Member_08 shiz = new Member_08(1, "館石静乃", 12, 7);
      Member_08 maya = new Member_08(2, "桜井真夜", 11, 9);
      Member_08 tema = new Member_08(3, "安堂てまり", 14, 8);

      //-----処理-------------
      int hp;
      hp = shiz.getNow_hp();
      shiz.setNow_hp(hp + 6);

      hp = maya.getNow_hp();
      maya.setNow_hp(hp -10);

      hp = tema.getMax_hp();
      tema.setMax_hp(hp -10);

      //---------------------

      System.out.println();
      System.out.println("NO." + shiz.getNo() + "\t" + shiz.getName());
      System.out.println("MAX_HP:\t" + shiz.getMax_hp());
      System.out.println("NOW_HP:\t" + shiz.getNow_hp());
      System.out.println("LIFE:\t" + shiz.isLife());
      System.out.println("------------------");

      System.out.println();
      System.out.println("NO." + maya.getNo() + "\t" + maya.getName());
      System.out.println("MAX_HP:\t" + maya.getMax_hp());
      System.out.println("NOW_HP:\t" + maya.getNow_hp());
      System.out.println("LIFE:\t" + maya.isLife());
      System.out.println("------------------");

      System.out.println();
      System.out.println("NO." + tema.getNo() + "\t" + tema.getName());
      System.out.println("MAX_HP:\t" + tema.getMax_hp());
      System.out.println("NOW_HP:\t" + tema.getNow_hp());
      System.out.println("LIFE:\t" + tema.isLife());
      System.out.println("------------------");
    }
}

Member_08クラスの問題点は、
値を Getterでオブジェクトの外に取り出して
オブジェクトの外で処理した結果を、Setterでオブジェクトに戻している点です。
Driver_08クラスの「処理」と書かれた部分で、
複数のインスタンスの値が入り乱れていて、値を取り違えるリスクが高いのです。
このような書き方は危険です。
カプセル化とは言えません。

値に変更を加える処理は、アクセスメソッドにすべて書き込むのが基本です。
処理をオブジェクトの中で完結して、
極力値をオブジェクトの外に出してはいけません

コマンドライン
ws>javac -encoding UTF-8 Driver_08.java
ws>java Driver_08

NO.1    館石静乃
MAX_HP: 12
NOW_HP: 12
LIFE:   true
------------------

NO.2    桜井真夜
MAX_HP: 11
NOW_HP: 0
LIFE:   false
------------------

NO.3    安堂てまり
MAX_HP: 4
NOW_HP: 4
LIFE:   true
------------------
プログラム自体は正常に動作しています。




5.値を取り出さずオブジェクトの中で処理する


せっかくカプセル化で値を守っているのですから、可能な限り値は取り出さないで、
処理をクラスの中に書きこみましょう。

//Member_09.java
class Member_09{
    public final int no;
    private String name;
    private int max_hp;
    private int now_hp;
    private boolean life=true;

    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }

    /*レベルアップ*/
    public void addMax_hp(int point); {
      max_hp += point;
      hpCheck();
    }

    /*回復 ダメージ*/
    public void addNow_hp(int point);{
      now_hp += point;
      hpCheck();
    }

    /*HPの整合確認 private*/
    private void hpCheck(){
      max_hp = max_hp<1 ? 1 : max_hp;
      now_hp = now_hp>max_hp ? max_hp : now_hp;
      if(now_hp<0){
          now_hp = 0;
          life = false;
      }
    }

    /*表示*/
    public void print(){
        System.out.println();
        System.out.println("NO." + no + "\t" + name);
        System.out.println("MAX_HP:\t" + max_hp);
        System.out.println("NOW_HP:\t" + now_hp);
        System.out.println("LIFE:\t" + life);
        System.out.println("------------------");
    }

    /*コンストラクタで初期化*/
    public Member_09(int no, String name, int max_hp, int now_hp){
        this.no = no;
        this.name = name;
        this.max_hp = max_hp;
        this.now_hp = now_hp;
        hpCheck();
    }
}

変更点
Driverクラスの処理を Memberクラスの中に
  • addNow_hp(int point)
  • addMax_hp(int point)
  • hpCheck()
  • print()
として記述しました。

変数 no は、public finalで変更不可で公開しました。

Getter/Setterメソッドを利用しているのは
  • getName()
  • setName(String name)
のみになりました。


addNow_hp(int point)addMax_hp(int point) のメソッド名は、
値を直接引数とするのではなく変化量を引数としているので、あえて Setterにしていません。
この二つのメソッドは、自身のメンバにしかアクセスできない為、
別のインスタンスの値を間違って使用すること自体が不可能です。



Member_09クラスを使用する場合、以下のようになります。
Driver_09クラスには、Member_09クラスの変数の値が出てきません。
Driver_08クラスと比較してみて下さい。
//Driver_09.java
class Driver_09{
    public static void main(String[] args){

      Member_09 shiz = new Member_09(1, "館石静乃", 12, 7);
      Member_09 maya = new Member_09(2, "桜井真夜", 11, 9);
      Member_09 tema = new Member_09(3, "安堂てまり", 14, 8);

      //-----処理-------------

      shiz.addNow_hp(6);
      maya.addNow_hp(-10);
      tema.addMax_hp(-10);

      //---------------------
      shiz.print();
      maya.print();
      tema.print();

    }
}

今度はどうだろう。
Memberクラスの堅牢性は
段違いだ。

なるほど。
Driverクラスにあった処理を
Memberクラス側に移したんですね。
Memberクラスは利用する人に
優しいわね。
コマンドライン
ws>javac -encoding UTF-8 Driver_09.java
ws>java Driver_09

NO.1    館石静乃
MAX_HP: 12
NOW_HP: 12
LIFE:   true
------------------

NO.2    桜井真夜
MAX_HP: 11
NOW_HP: 0
LIFE:   false
------------------

NO.3    安堂てまり
MAX_HP: 4
NOW_HP: 4
LIFE:   true
------------------
実行結果は同じです。



6.Getter/Setterの真意


カプセル化の重要な点
メンバ変数を使う処理は、メンバ変数が所属するオブジェクト内に書きましょう。

Getter/Setterメソッドの真意
アクセスメソッドは、値を得るGetterと、値を代入するSetterの書き方があるので
該当する場合はこの書き方に沿ってください。

ご注意
すべての値にGetter/Setterメソッドをつけて、
値をインスタンスの外に出して処理するのは、カプセル化の目的と逆行します。



お疲れ様でした。




© 2019 awasekagami