質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.03%

JAVA コンストラクタとsetterって同じことしてません?

解決済

回答 8

投稿 編集

  • 評価
  • クリップ 5
  • VIEW 8,180

kuzurotto

score 403

ふと気になったんですが、下記のようなnameを持つクラスがあるとして

①と②って同じことしてませんか?
同じことしてるなら別にsetterとかいらなくて
むしろgetterだけでいいような気が刷るのですが、どう思われますか?

public class Test {

    private String name;

    //①
    pblic Test(String name){
    this.name = name;
    }

    //②
    public void setTest(String name){
        this.name = name;
    }
}

ふと気になったので質問してみたら結構回答つきました。

思いついたのを今作ってみたのですが、要は使い分けとしてはこんな感じでいいですかね?
※思いつきでやってみたのでロジックは完璧ではありません。

package example;



public class Character {
    private String name;
    private int lv,hp,mp,atk,def;
    private int exp;//敵1体撃破時の経験値
    private int monsterNum;//敵の数
    private int expAll;//積算経験値

    public Character(String name) {
        //名前を決めてキャラを生成
        //初期値は下記の通り
        this.name = name;
        this.lv = 1;
        this.expAll = 0;
        this.hp = 50;
        this.mp = 15;
        this.atk = 35;
        this.def = 20;
    }

    //エンカウント
    public void enk(int i) {
        this.exp = 60;//敵1体が持つ経験値
        this.monsterNum = i;//敵の数
        this.expAll += (this.exp*this.monsterNum);//バトルで得た経験値を積算していく
    }

    //とりあえずsetterを使う場面として
    public void setLv(int lv){
        if(this.expAll > 500) {
            this.lv = lv;
        }
    }

    public static void main(String[] args) {
        Character chara = new Character("rento");
        System.out.println("名前:" + chara.name);
        System.out.println("Lv:" + chara.lv);
        System.out.println("HP:" + chara.hp);
        System.out.println("MP:" + chara.mp);
        System.out.println("攻撃力:" + chara.atk);
        System.out.println("防御力:" + chara.def);

        System.out.println(chara.name + "は敵と遭遇した。");
        System.out.println(chara.name + "は、超メラゾーマを唱えた!");
        System.out.println("敵に993,457,803,457,834のダメージを与えた!");
        System.out.println("敵は二酸化炭素となった!!!");
        chara.enk(1);
        System.out.println(chara.name + "は" + (chara.exp*chara.monsterNum) + "というちっぽけな経験値を手に入れた。");
        chara.setLv(2);

        System.out.println(chara.name + "は魔物の群れに囲まれた。");
        System.out.println(chara.name + "は、大地を砕き空を割った!!!");
        System.out.println("敵は二酸化炭素になることすら許されない程のダメージを受けた!");
        chara.enk(50);
        System.out.println(chara.name + "は" + (chara.exp*chara.monsterNum) + "というそこそこの経験値を手に入れた。");
        chara.setLv(2);

        System.out.println("現在の総経験値は" + chara.expAll + "です。");

        System.out.println("冒険後のステータスは下記の通りです。");
        System.out.println("名前:" + chara.name);
        System.out.println("Lv:" + chara.lv);
        System.out.println("HP:" + chara.hp);
        System.out.println("MP:" + chara.mp);
        System.out.println("攻撃力:" + chara.atk);
        System.out.println("防御力:" + chara.def);

    }

}

名前:rento
Lv:1
HP:50
MP:15
攻撃力:35
防御力:20
rentoは敵と遭遇した。
rentoは、超メラゾーマを唱えた!
敵に993,457,803,457,834のダメージを与えた!
敵は二酸化炭素となった!!!
rentoは60というちっぽけな経験値を手に入れた。
rentoは魔物の群れに囲まれた。
rentoは、大地を砕き空を割った!!!
敵は二酸化炭素になることすら許されない程のダメージを受けた!
rentoは3000というそこそこの経験値を手に入れた。
現在の総経験値は3060です。
冒険後のステータスは下記の通りです。
名前:rento
Lv:2
HP:50
MP:15
攻撃力:35
防御力:20

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 8

+6

①と②って同じことしてませんか?

内部的に行っている処理は同じですが、クラス利用側から見たときには異なる意味を持ちます。

  • ①コンストラクタによるメンバ変数代入は、オブジェクト生成と同時に行われます。質問にあるクラスはデフォルトコンストラクタを提供しないため、クラスTestにとって「nameという属性指定は必須であり、既定値(デフォルト値)は存在しない」という意図となります。
  • ②setterメソッドによるメンバ変数代入は、オブジェクトの生成後にいつでも「nameという属性値を変更できる」という設計を反映したものです。

後者setterメソッドを提供することは、クラスTestは「ミュータブル(Mutable)/可変なクラスである」ことを意味します。
反対に、全てのメンバ変数をfinal宣言し、コンストラクタでのみメンバ変数を設定し、setterメソッドを提供しないクラスは、イミュータブル(Immutable)/不変なクラスと呼ばれます。クラスをイミュータブルとすることは、Javaのクラス設計においては非常に重要な意味を持ちます。

Javaの理論と実践: 可変性か、不変性か?」も参考にどうぞ。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:06

    質問追加しましたが、可変なクラスというのは、追加記述のLvがそうということでしょうか?

    キャンセル

  • 2016/09/15 09:27

    ミュータブル/イミュータブルの別はクラス単位で考えます。一つでも変更可能なフィールドを含む場合はミュータブルなクラスです。追記されたCharacterクラスはミュータブルです。

    キャンセル

checkベストアンサー

+4

処理としては一緒ですが、使い方は違います。

コンストラクタは、クラスをインスタンス化(初期化)するときに呼ばれるため、
そのインスタンスが生成されるときに必ず必要なパラメータを渡します。
※もちろん必要でなくても、初期化時にあった方がいいものでもいいのですが

一方、セッターはインスタンスが生成された後なら、いつでも何度でも実行できるので、
クラスのプロパティを変更したいときに好きに使えばいいと思います。

あまりいい例ではないですが、標準ライブラリでいうとLevelクラスがコンストラクタはあるけど、セッターがない例です。
このクラスはログのレベルですが、ログのレベルの名前や値は、
あとから変えられると困るし、初期状態で知りたいので、コンストラクタでのみ設定できるようになっているんだと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:07

    なんとなくわかってきました!ありがとうございます!

    キャンセル

  • 2016/09/15 05:20

    解答欄に簡単な例をつけてみました。

    キャンセル

+1

要件とか仕様とか方針とか信仰とかでsetter、getterの要る要らないは変わります。
基本的にはコンストラクタで設定した名前を以降の処理で変更する可能性があれば必要、
なければ不要としておけばいいのではないでしょうか。

setterはいらないよ派
・オブジェクトはイミュータブルであるべき
・名前を変えるときは別インスタンス

setterはいるよ派
・名前だけ変えればいいならsetterでやろう
・インスタンスを作るのはコストが高い

コンストラクタはデフォルトだけ派(setterはいるよ派亜種)
・フレームワークでインスタンス作って値のセットまでしたいとか

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:09

    スタンスは人それぞれってことですね、ありがとうございます!

    キャンセル

  • 2016/09/15 05:33

    マシンスペックが向上した昨今では,どちらかというとイミュータブルを支持する声が大きくなっている印象は受けます.使いやすさを犠牲にしてまで適用する必要はないと思うので,「無理せずできるならイミュータブルにしよう」ぐらいがいいかと思います.

    キャンセル

+1

①は、呼び出し元のコードを簡略化できます。

Test test1 = new Test("hoge");

このコンストラクタがない場合は次のようなコードになります。

Test test1 = new Test();
test1.setName("hoge");

他の回答者さんも回答している通り、プロジェクトの方針であったり設計上の好みであったりするので、
どちらが良いというのはありません。

前者はコードを簡略化できますが、ぱっと見てコンストラクタに渡してる値が何なのかわかりません。
後者はコードが増えますが、ぱっと見て何をしているか明確にわかります。

個人的な設計方針としては、オブジェクトに名前をつけ、その名前は不変であるべきというクラスの場合は、
setterを実装せずに、コンストラクタで名前を渡すようにします。
例えばスレッドクラス等は一度つけたら変わりません。

データベースのテーブルデータをそのままクラスにマッピングする場合などは、コンストラクタは空にするか、
全フィールドを引数に持つコンストラクタを作ったり、ResultSetをコンストラクタにもつものはもちろん、
全てのフィールドにsetter, getterを準備するでしょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:08

    プロジェクト方針ってことですね、ありがとうございます!

    キャンセル

+1

コンストラクタとsetterって同じことしてません?

コンストラクタは初期化セッターは設定のためにあり、役割が別です。

最近は関数型言語の流行もあって、「不変」設計の重要性が言われています。
ただ、それはほかの回答者の方がすでに指摘されていますね。

そこで私は、オブジェクト指向の基本の基本、
カプセル化としてのコンストラクタを説明したいと思います。


コンストラクタがなくても、セッターのみで設定はできます。

しかしたとえば、newでインスタンスを生成した時に、
フィールドの変数が未初期化、「null」の状態になってます。

すると、セッターを忘れてしまい、nullでエラーが出る可能性が出ます。
人間の注意力で防げるのは99%までで、けっして100%予防できません。

しかし、必ずコンストラクタで初期化すれば、100%初期化忘れを防げます
なぜなら、引数を渡さないとコンパイルエラーになるからです。
(引数のかわりにコンストラクタ内でデフォルト値を設定してもいいです)

これはあまりに細かいように思えるかもしれませんが、
大規模開発やミッションクリティカルな開発では重要です。

じっさい、どこから出てきたか分からないnullに困らされたり、
そのnullチェックを何回もやって煩雑になるケースは多そうです。


補足しておくと、フィールドの宣言時にも初期化はできます。
ただし値が固定なので、これだけで済ますと別の問題があります。

コンストラクタの場合は、クラス利用側が初期化値を指定できるのと、
冒頭で言った不変パターンに使えるのがメリットです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:05

    ありがとうございます!

    キャンセル

+1

テクニカルでない自明な回答を掲載します。

オブジェクトの属性を、状態遷移に着目して以下の2種類に分け、値の設定方法を考える。

1. 状態属性
状態を表すインスタンス変数を状態属性とする。状態遷移させるには、状態属性の値を変更する。
そのために、setterを定義して値を変更するとともに、遷移後の状態に矛盾のないことを保証する。

例: 申請状態(作成、申請、承認、差戻)、婚姻状態(未婚、既婚)など値が変わる属性

状態属性にはコンストラクタ内部で初期値を与える。
ただし、生成時に動的にしか決定できない状態属性の値は、コンストラクタの引数で設定しても良い。

2. 不変属性
オブジェクトの生存期間中に値が変わらないインスタンス変数を、不変属性とする。
不変属性は、コンストラクタで一度だけ設定し、setterは定義しない。
状態属性を変更して状態遷移させる場合は、不変属性が破壊されないことを保証する。

例: ID、生年月日、性別?など値が変わらない属性

不変属性だけ、状態属性だけのオブジェクトがあっても良い。

3. 原則
不変属性は、コンストラクタの引数で設定する。setterは定義しない。
状態属性は、コンストラクタでデフォルト値を設定。setterで値を変更する。

この原則はほぼあてはまると思いますが、要件や技術的な制約によって変更されることがあります。例えば、

・Class.newInstance()でインスタンスを生成したいのなら引数なしのコンストラクタは必須。引数付きの初期化メソッドを別に用意するか、PropertyDescripterを利用して属性を変更するために、setter/getterのペアを定義する。

など。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

コンストラクタは1度しか呼べない性質から、
インスタンスを作成した後で値を変更されたくない場合は①を、
インスタンスを作成した後で値を変更したい場合は②を
採用するよう区別して設計しています。(僕は)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/14 23:09

    ありがとうございます!

    キャンセル

0

コンストラクタは初期化です。
そのオブジェクトを構成するのに必要な要素を与えてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる