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

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

新規登録して質問してみよう
ただいま回答率
85.53%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

8回答

22848閲覧

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

earnest_gay

総合スコア615

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

5クリップ

投稿2016/09/14 05:16

編集2016/09/14 14:00

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

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

java

1public class Test { 2 3 private String name; 4 5 //① 6 pblic Test(String name){ 7 this.name = name; 8 } 9 10 //② 11 public void setTest(String name){ 12 this.name = name; 13 } 14}

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

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

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

mpyw👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答8

0

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

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

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

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

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

投稿2016/09/14 05:52

編集2016/09/14 05:56
yohhoy

総合スコア6187

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:06

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

2016/09/15 00:27

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

0

ベストアンサー

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

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

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

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

投稿2016/09/14 05:49

編集2016/09/14 20:20
popobot

総合スコア6586

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:07

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

2016/09/14 20:20

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

0

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

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

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

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

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

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

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

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

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

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

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

など。

投稿2016/09/14 14:39

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

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


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

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

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

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

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

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


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

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

投稿2016/09/14 12:30

LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:05

ありがとうございます!
guest

0

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

投稿2016/09/14 05:49

pinpikokun

総合スコア376

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

Test test1 = new Test("hoge");

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

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

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

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

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

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

投稿2016/09/14 05:44

moonphase

総合スコア6621

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:08

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

0

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

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

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

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

投稿2016/09/14 05:33

kopio

総合スコア487

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:09

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

2016/09/14 20:33

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

0

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

投稿2016/09/14 05:34

oriduru

総合スコア36

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

earnest_gay

2016/09/14 14:09

ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問