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

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

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

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

Q&A

解決済

5回答

8376閲覧

java カプセル化について解説していただけないでしょうか

BSS_sapporo

総合スコア44

Java

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

0グッド

1クリップ

投稿2016/04/07 09:58

javaのカプセル化について勉強をしています。
以下の仕様で2.部分のコードを自身で記述するような問いなのですが、なぜこの仕様で2.のようなコードになるのかが理解ができないので、解説をお願いできないでしょうか。

また、このまま1.2.を繋げて記述すると、2.の2行目「public class Cat {」のところで「public型はそれ独自のファイル内に定義されなければなりません」と表示がでてしまいます。
初歩的な質問で申し訳ありませんが、こちらについても解説していただけないでしょうか。

仕様
<パッケージ名>
Shop
<クラス名>
Cat
<フィールド>
String syurui 猫の種類
String seibetsu 猫の性別
int toshi 猫の年
<コンストラクタ>
Cat() ・・・フィールドsyuruiは「スコティッシュフォールド」、seibetsuは「メス」、toshiは0で初期化
Cat(String tSyurui, String tSeibetsu, int tToshi) ・・・各引数でフィールドを初期化
<メソッド>
String getSyurui() 猫の種類を返す
String getSeibetsu() 猫の性別を返す
int getToshi() 猫の年を返す

1.2.の実行結果
スコティッシュフォールドのメス、0歳を買いました。
アメリカンショートヘアーのオス、0歳を買いました。
が出力される。

----------以下からコード----------
1.
package Shop;
public class CatShop {
public static void main(String[] args) {
Cat myCat = new Cat();
CatShop.buyCat(myCat);

myCat = new Cat("アメリカンショートヘアー","オス", 0); CatShop.buyCat(myCat); } static void buyCat(Cat tCat) { System.out.println(tCat.getSyurui() + "の" + tCat.getSeibetsu() + "、" + tCat.getToshi() + "歳を買いました。"); }

}

2.
package Shop;
public class Cat {

String syurui; String seibetsu; int toshi; Cat() { syurui = "スコッティッシュフォールド"; seibetsu = "メス"; toshi = 0; } Cat(String tSyurui, String tSeibetsu, int tToshi) { syurui = tSyurui; seibetsu = tSeibetsu; toshi = tToshi; } String getSyurui() { return syurui; } String getSeibetsu() { return seibetsu; } int getToshi() { return toshi; }

}

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

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

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

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

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

guest

回答5

0

まず、フィールドを private にして、
public な get, set のアクセサを用意することがカプセル化ではありません。

インスタンスの実データを取得できて、実データを書き換えれるので
public なフィールドにしているのと同じことです。

今回のだと、フィールドは不変オブジェクトであるStringと
値渡しをするプリミティブ型のみ。
初期化はコンストラクタのみ。
取得はget経由で、setは用意しないとなっており、
データの保護は出来ているので、カプセル化と言えると思います。

ただし、setを用意しないからデータを保護できるとは限りません。
例えば以下。(通常こんなことはしませんが。。。)

Java

1class Pair { 2 3 private String[] array; 4 5 /** 6 * コンストラクタ 7 */ 8 public Pair(String first, String second) { 9 this.array = new String[] { first, second }; 10 } 11 12 /** 13 * ペアを返す 14 */ 15 public String[] getPair() { 16 return array; 17 } 18 19 /** 20 * 1つ目を返す 21 */ 22 public String getFirst() { 23 return this.array[0]; 24 } 25 26 /** 27 * 2つ目を返す 28 */ 29 public String getSecond() { 30 return this.array[1]; 31 } 32 33 @Override 34 public String toString() { 35 return String.format("[%s, %s]", this.array[0], this.array[1]); 36 } 37} 38 39public class Main { 40 public static void main(String[] args) { 41 Pair pair = new Pair("あ", "ア"); 42 System.out.println(pair); // [あ, ア] と表示 43 44 pair.getPair()[0] = "か"; 45 System.out.println(pair); // [か, ア] と表示 46 } 47}

このように、参照のコピーを渡すようなもの(今回は配列)を
フィールドに持っている場合、getしか用意していなかったとしても、
インスタンスが持つ実データを返している時点で、データは書き換えられます。

###カプセル化とはデータと実装の隠蔽のことです。

そのクラスを使う人は、そのクラスがどんなデータを持っていようと
どんな実装になっていようと知る必要はない。
public なメソッドの使い道だけが分かっていれば使えるクラス
がカプセル化されたクラスです。

例えば、java.lang.String を見てみると

java

1private final char value[];

とあります。
文字列の正体は char の配列のようです。
Javaに文字列なんか存在しなかったわけです。

こんな事を知らなくても、Javadoc を見るだけで
indexOf ができたり subString ができたり、あたかも本当の文字列のように扱えている。
すごくカプセル化されたクラスです。
そして、当然この char の配列をそのまま取得できるような
メソッドはありません。

ついでに、java.util.HashMap を見てみます。

java

1static class Node<K,V> implements Map.Entry<K,V> { 2 final int hash; 3 final K key; 4 V value; 5 Node<K,V> next; 6 78}

内部にまたクラスがありますね。このクラスは公開されていません。
この Node というクラスが key と value を持ってますね。
ついでに次の Node も持ってますね。
深く見てないので分かりませんが、
1つ1つの Node が次を持つことで、あたかも1つのものに見えているようです。
当然、getNode など公開されておりません。

こんなどうでもいい事を知らなくても、
普通に put したり get できればいいんです。
これがカプセル化です。
フィールドを private にするとか set を用意しないとかっていうのは、
カプセル化のうちの1例でしかありません。

ちなみに、この Node クラスが別の Javaファイルで public なクラスとして作成されていたら、
最悪なわけです。
こんな内部でしか使わないクラスを 公開してどうすんの?ということになります。
これは実装の隠蔽(カプセル化)ができていないということです。

カプセル化されたクラスというのは、非常に使いやすいものになるため、
プログラマーはすごく意識しなければならないところですね。
set がないからカプセル化ではありません。

投稿2016/04/07 12:49

編集2016/04/07 13:07
root_jp

総合スコア4666

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

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

BSS_sapporo

2016/04/08 00:35

詳しく解説していただいてありがとうございます。 e-ラーニングで学習しているのですが、ご回答のように噛み砕いての説明はなかったので助かりました。
swordone

2016/04/08 01:05

>当然この char の配列をそのまま取得できるようなメソッドはありません。 toCharArray()「…」 まあ確かにコピーですけれども
guest

0

ベストアンサー

カプセル化はクラス内のメソッドやフィールドにアクセス制限や更新制限を掛けることです。
例えば下記のようなコードがあったとします。

Java

1class Cat { 2 public String syurui; 3 public String seibetsu; 4 public int toshi; 5}

全てのフィールドがクラスの外から丸見えです。参照変数さえわかればアクセスができてしまいます。これではプログラム上のどこで変更されるかわからなかったり、無制限で変更することができてしまいます。

このような状況をなくすためにカプセル化を進め、下記のようなコードになりました。

Java

1class Cat { 2 private String syurui; 3 private String seibetsu; 4 private int toshi; 5}

クラスの外部からはアクセスできなくなり、情報が保護されます。しかし、カプセル化をしすぎたため、値の設定、初期化すらできません。そこでコンストラクタを使って初期化します。

Java

1class Cat { 2 private String syurui; 3 private String seibetsu; 4 private int toshi; 5 Cat(String tSyurui, String tSeibetsu, int tToshi) { 6 syurui = tSyurui; 7 seibetsu = tSeibetsu; 8 toshi = tToshi; 9 } 10}

これで、各値の初期化ができるようになりました。しかし、これでカプセル化がキツすぎるため設定した値が読み取りません。

各フィールドを、読み取るだけのメソッドを追加してクラスの外にフィールドを参照できるようにします。

Java

1class Cat { 2 private String syurui; 3 private String seibetsu; 4 private int toshi; 5 Cat(String tSyurui, String tSeibetsu, int tToshi) { 6 syurui = tSyurui; 7 seibetsu = tSeibetsu; 8 toshi = tToshi; 9 } 10 String getSyurui() { 11 return syurui; 12 } 13 14 String getSeibetsu() { 15 return seibetsu; 16 } 17 18 int getToshi() { 19 return toshi; 20 } 21}

こうして、最初は誰でも更新できたクラスが初期化時にしか値を設定できず、設定した値は変更できない読み取り専用のクラスになりました。
このようなクラス内の情報(フィールドやメソッド)の公開を最小限にした結果カプセル化されたクラスが出来上がります。

投稿2016/04/07 10:44

編集2016/04/08 02:22
yona

総合スコア18155

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

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

BSS_sapporo

2016/04/08 00:46

具体例まで入れて解説頂きありがとうございます。 段階ごとに理解することができて助かりました!
yona

2016/04/08 02:23

大事なアクセス修飾子に誤記があったので修正しました。ごめんなさい
guest

0

まずは2つ目の質問から.
1つのJavaクラスファイル(ソースコード)に対して1つのクラスというのは,Javaプログラミングで推奨されていることです.
また,クラス名とクラスファイル名は一致させるべきです.(CatクラスはCat.javaに記述)

Catクラスはsyurui,seibetsu,toshiというフィールド(変数)を持っています.
このクラスのフィールドにアクセスする方法はいくつかあります.
一番単純な例はこれです.

java

1Cat cat = new Cat(); 2System.out.println("種類は" + cat.syurui + "です.");

しかしこの実装では,クラス内のフィールドを直接参照するという点で問題があります.
例えばこんなこともできてしまいます.

java

1Cat cat = new Cat(); 2cat.shurui = "オランウータン"; 3System.out.println("種類は" + cat.syurui + "です.");

クラスが持っている値に直接アクセスできるため,その値を書き換えることもできます.
こういうことを許してしまうと,クラス外から本来想定しない値が書き込まれてしまい,
バグの原因となる可能性があります.

このようなことを防ぐために,カプセル化という手法が存在します.
①クラス外から直接値を変更されたくないフィールドには「private」「protected」などを付ける
②クラス外から値を取得する場合はgetXxx(),変更する場合はsetXxx()を経由させる
setXxx()では想定しない値が書き込まれないようにチェックする
(コンストラクタでも同様に値のチェックを行う)
この2つの原則によって,他のクラスやパッケージからのアクセスが
直接フィールドに行くのではなく,メソッドを通して行われるようにする,
というのがカプセル化です.
フィールドをメソッドというカプセルによって守っているわけです.

投稿2016/04/07 10:30

KenTerada

総合スコア751

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

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

BSS_sapporo

2016/04/08 00:52

カプセル化とは他のクラスなどから直接アクセスできないように守る、ということなのですね。 ご丁寧に解説いただきましてありがとうございました!
guest

0

あえて、一言でそれぞれをまとめてみました。

カプセル化

カプセル化は、外部に見せたくないものを隠し、外部から受け付けたくない入力は受け付けないようにする仕組みと思ってもらっていいです。(深く理解するのは、そのあとでも。)

publicなクラス

publicなクラス定義は1つのJavaファイルに1つというJavaの決まりです。
publicなクラスを複数定義するときは、その数だけ、ファイルを分ける必要があります。

投稿2016/04/07 13:04

Odacchi

総合スコア907

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

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

BSS_sapporo

2016/04/08 00:25

publicなクラス定義は1つのファイルにつき1つという決まりだったのですね。 そのため質問に記載したようなエラーが出ると…。 カプセル化についても理解できました! ご回答ありがとうございました。
guest

0

このまま1.2.を繋げて記述すると

public classはクラスと同名のソースファイルに1つしか定義できないので、

java

1//public class Cat { 2class Cat {

とすれば無問題。

■追記:

<フィールド>

フィールドの各変数がコンストラクタの引数と同じ内容であるので、「フィールドってのはstaticじゃないんだな」と判断できるので、

java

1//static String syurui; //<- ではなく 2String syurui; // 猫の種類

となるのです。

<コンストラクタ>
Cat() ・・・フィールドsyuruiは「スコティッシュフォールド」、seibetsuは「メス」、toshiは0で初期化
Cat(String tSyurui, String tSeibetsu, int tToshi) ・・・各引数でフィールドを初期化

引数つきのコンストラクタでは各フィールドの値を引数から、、引数なしのコンストラクタでは各フィールドの値を初期値設定しろってことですよ。

※引数なしのほうではつい

java

1Cat(){ 2 this("スコティッシュフォールド", "メス", "0"); 3} 4

とか書きたくなりますが。

<メソッド>
String getSyurui() 猫の種類を返す
(以下略)

各フィールドの値を「返す」と言われてますね。
返すんだから「return」でしょ?

java

1String getSyurui() { 2 return syurui; // <-猫の種類という意味のフィールド「shurui」を「return」してますね。 3}

。。。こんな感じでいいですかね?

投稿2016/04/07 10:03

編集2016/04/07 10:19
tkturbo

総合スコア5572

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

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

BSS_sapporo

2016/04/08 00:59

public classについて存じ上げませんでした。 ご回答ありがとうございます。大変助かりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問