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

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

ただいまの
回答率

88.78%

Javaのsetterとgetterの意義について

解決済

回答 13

投稿 編集

  • 評価
  • クリップ 5
  • VIEW 18K+

frippy

score 18

Javaで実装するときに、以下のようにフィールドをprivateにしてsetterとgetterを実装するということがあるかと思います。

public class Product {
    private String name;
    private Integer price;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getPrice() {
        return price;
    }
    public void setPrice(Integer price) {
        this.price = price;
    }
}

最初にこれを見たときは、わざわざフィールドをprivateにしてsetterとgetterを実装する意味が分からず、以下のようにフィールドをpublicで実装してしまった方がコードも短くなって読みやすいのではないかと思いました。

public class Product {
    public String name;
    public Integer price;
}

その後も、Javaでsetterとgetterを実装する理由について、周りのエンジニアの方々に質問したり、ググったりしてみたのですが、「Javaの仕様だから」、「オブジェクト指向だから」というような回答が多く、なかなか納得できる答えに出会えませんでした。

今では、私自身もなんとなくsetterとgetterを実装する習慣が付いてしまっているのですが、それでもsetterとgetterを実装する実質的なメリットはよく分かっていません。

この辺りの話題に詳しい方がいらっしゃったら、setterとgetterの意義について教えて頂けるとありがたいです。

よろしくお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 13

+6

発想としては、「外部に公開する仕様を、内部の実装とは関係なく決め打ちにできてしまう」ということがあります。

たとえば、メンバ変数として特定のルールに従った値を要求する場合、パブリックでそのまま代入するとその値を使う時点でしかチェックできませんが、setterを使えばその段階でチェックが可能となります。

また、変数という形では時々刻々と変わる値をその都度取得する、というような処理は実現することはできませんが、getterとすれば特に問題がありませんし、内部的に「処理して取得するもの」と「変数として持っているもの」とを区別することなく、外部から使えるようになります。

なお、C#やRubyなど、外から見れば変数代入に見えるけど、内部的にはアクセサを動作させるというような形で、明示的にアクセサを呼びださなくて済むようにした言語もあります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+5

このクラスに限って,あるいは,このような単純な情報の出し入れだけだったらおっしゃるようにgetter/setterを設けるメリットはないのかもしれません.
しかし,もっと多くの情報を扱うクラスで,他のクラスから勝手に操作されたくない情報というのはほぼ確実に出てきます.
例えば,Exceptionクラスには「原因」となるThrowableオブジェクトを格納するフィールドがあります.この仕組み上,1回原因を設定したあとは変更を加えられるのは望ましい状態であるとは言えません.このため,initCause(Throwable)というメソッドがあります.setで始まりませんが,実質setterです.
すでにこのメソッドで原因を入れていたり,Exceptionのコンストラクタで原因を入れていたりした場合は例外を発生させる仕組みになっています.

そして,このようなgetter/setter"だけ"を設けた場合,どのフィールドに対してgetter/setterを付けたか,付けなかったかをいちいち考えてプログラムしなければならず,非常に使いづらいクラスになるでしょう.それなら全てにgetter/setterを設けて,アクセス方法を統一したほうがわかりやすいですし,ミスも減ると思います.

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+2

私も深く理解している訳ではないのですが、JavaBeansに即してではないかと思います。

JavaBeansのルールを知る

以下の用途でJavaClassを使用する場合、getter/setterは必須になります。

・JSPのページからJavaClassをアクセスする
・JSFのBackingBean実装
・JavaEEにてDBアクセス用にModel(EntitiyBean)を作成する

フレームワークやライブラリを利用する際、スクリプトの記載からsetter名/getter名に変換
してインターフェースとする用法が多いようです。


投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+2

他の言語から Java を見た場合の意見です。

Java は getter/setter(メソッド呼び出し)とフィールドアクセスの記法を揃えられません。
そのことから書き方を揃えるために getter/setter に寄せるのが一般的になっているのではないかと思います。

メソッド(getter/setter)とフィールドアクセスの違いは、アクセス時に処理をいれこめるかどうかの違いです。
単に値を運ぶだけならフィールドアクセスで良いのですが、必要時に計算して得る値となるとメソッドで実装するしかありません。

メソッドとフィールドアクセスには明確な構文の違いがあります。

value = a.getField();
a.setField(value);

value = a.field;
a.field = value;

この2つを混在させたくないから getter/setter に寄せるのではないかと思います。
1つに統一したいのはなぜかというと、

・単純に全体の統一感を維持したいから。
・もしも処理が必要になったときに、メソッドに変更するしかなく、その場合は全てのアクセス箇所を書き換える必要が出てくる。

というのが主な理由だと思います。

これが他の言語になると話は変わってきます。

getter/setter 定義用の専用の構文が用意されていて、
getter/setter とフィールドアクセスの書き方が統一されていて切り替え可能になっていたりします。
スクリプト言語である Ruby や C# なんかでも使えます。Java の派生言語である Scala なんかでも使えたような気がします。

そうした場合は段階的に最初はフィールドを直接アクセスさせておいて、必要になったら getter/setter に書き換えるということがしやすいです。

Java はそれがしずらいので、そろえるなら getter/setter にそろえるというのが一般的になっているように思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+2

確かにオブジェクト指向だからと言われるとわからないですよね。
基本的には以下のようなメリットがあるからでしょうか。
・オブジェクトの呼び出し元はオブジェクトの状態を意識せずに扱える。
→getterやsetterを定義するルールにしておけば、統一がしやすいってこともありますね
・オブジェクト内でしか使わない情報を隠せる。
→そのフィールドを他の処理でも使う場合などに直接アクセスされると困る場合など
・呼び出し元を意識せずに変更がしやすい。
→呼び出し元が知っているのはgetterとsetterだけなので、実際のgetterとsetterの処理が変更しやすい
・継承などを行ってもアクセスの方法が変わらない。
→継承先のsetterやgetterの処理を変えることもできる。

また、基本的にはオブジェクト間はメッセージをやりとりするような感覚で扱うので、
例えば、Aという人を表すオブジェクトに対して、「Aさん、名前!」というより「Aさん、名前を教えて!」という方が自然に扱えるからというのも考え方の一つとしてあっても良いかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+2

はっきり言ってしまえば、getter/setterを書く方法と、当該フィールドをpublicにする方法とは、一長一短があり、議論の対象です。Kent Beckの「Smalltalkベストプラクティスパターン」などでも、両方の流儀の良さを述べていたと記憶します。

しかし、ことJavaに限っていれば、getter/setterを書く方法が主流であり、ほぼ議論の余地はありません。これは単純にJava Bean仕様によってそのように決めたので、特別な事情のないかぎり、その仕様に従っておく、ということが慣例になっているということです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/03/30 16:47

    もうちょっと補足するとリフレクションを簡単に使うための工夫です。

    getter/setterを書くことにしておけば、getMethods()することでgetter/setterも入手できます。しかし、getter/setterを書かない場合はgetFields()しなければなりません。リフレクションを使うときの手間が大きく違いますね。

    キャンセル

  • 2015/03/31 23:33

    回答ありがとうございます。

    >>リフレクションを使うときの手間が大きく違いますね。

    すみません。
    この辺りが無知なので、もう少し詳しく教えて頂けるとありがたいです。

    例えば、

    public class Product1 {
    public String name;
    public Integer price;
    }

    というクラスと

    public class Product2 {
    private String name;
    private Integer price;
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public Integer getPrice() {
    return price;
    }
    public void setPrice(Integer price) {
    this.price = price;
    }
    }

    というクラスがあるとします。

    このとき、以下のように、Product1に関してはgetFields()を使って、Product2に関しては
    getMethods()を使えば、どちらでもリフレクションが可能かと思います。

    public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
    for(Field field : Product1.class.getFields()) {
    System.out.println(field.getName());
    /*
    * <出力結果>
    * name
    * price
    */
    }

    for(Method method : Product2.class.getMethods()) {
    System.out.println(method.getName());
    /*
    * <出力結果>
    *
    * getName
    * setName
    * getPrice
    * setPrice
    * wait
    * wait
    * wait
    * equals
    * toString
    * hashCode
    * getClass
    * notify
    * notifyAll
    */
    }
    }
    }

    >> 手間が大きく違う
    というのは、どのような場面でリフレクションを使用する場合のことを想定しているのか、教えて頂けるとありがたいです。
    よろしくお願いします。

    キャンセル

  • 2015/04/01 05:10

    getMethods()とgetFields()の両方を扱うより、getMethods()だけを扱う方が手間が小さいです。半分以下になりますね。

    また、リフレクションを使うのは、メソッドやフィールドの名前を取得するだけでなく、そこからユーザにそれを示したり、設定ファイルと突き合わせたりして、実際にいずれかのメソッドやフィールドにアクセスすることになると思いますが、そのときもメソッドとフィールドでは、ユーザに選択肢として表示するにも、実際にアクセスするときにも、それぞれに行なう必要がありますね。

    キャンセル

+2

以前も同じような質問があったので、一応載せておきます。

https://teratail.com/questions/2863

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/03/31 23:22

    ご指摘ありがとうございます!
    こちらのやり取りも参考にさせて頂きます。

    キャンセル

+2

getterとsetterがあることによって、例えば、下記のようにpriceに0以下の値をセットすることを禁止したりもできます。
もし、フィールドがpublicだったらこのような拡張ができませんね。

public class Product {
    private String name;
    private Integer price;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getPrice() {
        return price;
    }
    public void setPrice(Integer price) {
        if (price.intValue() >= 0)
            // priceが0以上の場合はそのまま代入
            this.price = price;
        } else {
            // 0以下の場合は例外をスローする
            throw new HogeException("price is NOT positive number.");
        }
    }
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/03/16 14:37

    ありがとうございます。たしかに、バリデーションを追加したいときなどは、setterにしたメリットが分かりやすく現れますね。

    キャンセル

checkベストアンサー

+1

Javaに限ってのお話ですが、他の方が答えられているように、大半は、JavaBeansの仕様に従っているだけだと思います。
他のオブジェクトから操作されたくないような場合は、getterはまだしも、setterに関しては、この記事にあるように、単純に薄っぺらいラッパーではなく、別のかたちのメソッドになるように思えます。

また、インスタンスフィールドを公開しない理由としては、疎結合にするというのもあります。
疎結合にすることで、JMockitなどで、クラスをMock化する際に、accessorメソッドがあった方が、Mock化しやすいという利点もありますが、現実には、インスタンスフィールドを直接書き換えるなんて、リフレクションを使わない限りは、初心者くらい(バグ出しそうで、怖くて、普通はやらない)だと思うので、コンストラクターで初期化して、書き換えないような場合はpublic final、リフレクションで初期化してから使われるような場合は、publicにして公開してしまったりしています。

Javaの定石では、accessor(setter/getter)を用意するのですが、不要であれば直接公開もあり と言ったところでしょうか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/03/31 23:55

    回答ありがとうございます!

    >>コンストラクターで初期化して、書き換えないような場合はpublic final

    例えば以下のような感じですね。

    public class Product {
    public final String name;
    public final Integer price;

    public Product(String name, Integer price) {
    this.name = name;
    this.price = price;
    }
    }

    こういう実装は今まで気づかなかったので、目から鱗です。
    ありがとうございます!

    キャンセル

+1

私もあまり詳しいわけではありませんが、
オブジェクト指向のカプセル化という考え方に関係あると認識しています。

全てpublicのメンバ変数だと、外部で何か修正が加わった際にクラスの仕様をよくわかってない人が
入れてはいけない場所に入れてはいけない値を入れてしまうリスクが発生してしまいます
数値を期待している変数なのに文字列を突っ込んでしまうとか…
まずはAという変数に値を入れて、処理結果がBに入るのに、いきなりBに代入してしまうとか…

期待していない変数にいきなり文字列を突っ込まれて
「あれ?エラー…。ここで文字列を数値に変換してやる必要があるんじゃね?あれ、今度はこっちでエラー…じゃぁここをこうして…」
などと、クラスの仕様を理解してない人がコードを引っ掻き回して、せっかくきっちり作っていたのにソースコードがボロボロになってしまうかもしれません。

そういった場合もprivateでとっておけば、セッターがなければ代入できませんし、
セッターを通していれば、セットする段階で型などを判定してやればよいです
また、そういうコードをアクセサに仕込んであれば、あとでこのクラスを使おうと思った人が見たとき、
「あ、このアクセサには数値が入ることを期待しているんだな」とかわかりやすいと思います

多人数で大きなシステムを開発する上で非常に重要なのではないかなと思います

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

例えばDBと連携する自前のクラスを作るとします。そのクラス内にDBのオブジェクトをprivateな変数として持っておけば、そのクラス内からしか操作されない事が保証されますよね?
そうする事でprivateの意味が出てくるんじゃないかな?
カプセル化とか隠ぺいとかよく言われてるけど、プログラマー同士のメッセージ的な意味だと自分では思っています。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/03/30 22:38

    でもDBオブジェクトのgetter、setterあったら外でちゃうから意味ない(笑)
    回答になってなかった

    キャンセル

+1

カプセル化。
基本的にインスタンス変数は公開してはいけません。
どこでどうかえられたか管理できず煩雑になるため。
Beanのようなクラスではメリットはあまり見受けられないと思いますが、
Interfaceなどスコープを小さくする場合にメリットがあると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

クラスのプロパティに対するsetterメソッド(ミューテータ)、getterメソッド(アクセサ)の存在理由としては、そのクラスのプロパティを別のクラスから利用できるのがgetterだけ、つまり内容を取得するのを許可し、setterは許可しない、のように利用制限するために用います。

ただ単純にデータベースの検索結果や画面からの入力項目を格納する目的のクラス(DataTransferObjectと呼称されているものです)ならば、常にgetter/setterを両方使いますので、これらのメソッドは不要で、public化しても特に大きな問題にならないでしょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/03/16 14:35

    ありがとうございます!非常に明快に説明して頂き、腑に落ちました。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る