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

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

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

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

Q&A

解決済

3回答

1254閲覧

クラス通しのかかわりについて

退会済みユーザー

退会済みユーザー

総合スコア0

Java

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

0グッド

0クリップ

投稿2016/12/20 17:26

下記のコードセわからないところがあります

java

1public class Person { 2 private final Label _name; 3 private final Label _address; 4 5 public Person(Label name, Label address){ 6 this._name = name; 7 this._address= address; 8 } 9 10 public void display(){ 11 if(_name != null){ 12 _name.display(); 13 } 14 15 if(_address != null){ 16 _address.display(); 17 } 18 } 19}

java

1public class Label { 2 private String _label; 3 4 public Label(String label){ 5 this._label = label; 6 } 7 8 public void display(){ 9 System.out.println("display:" + _label); 10 11 } 12 13}

わからない部分は次の2点です。
①Personクラスの_nameと_addressにfinalをつける必要がありますか?
_nameと_addressは定数ではないと思い、疑問に感じています。

②PersonクラスでLabel型の_nameと_addressを宣言していますが、継承等を行っていないのに宣言して、コンパイルエラーが出ない仕組みがよくわかっていません。PersonクラスでLabel型は使えるものでしょうか?
また、継承等を行わなくてもいいのでしょうか?

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

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

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

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

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

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

guest

回答3

0

ベストアンサー

違った視点から

①Personクラスの_nameと_addressにfinalをつける必要がありますか?
_nameと_addressは定数ではないと思い、疑問に感じています。

staticではないフィールドにprivateを設定するとコンストラクタでしか代入できなくなります。全てがfinalなフィールドのオブジェクトは**不変(immutable)**オブジェクトともいわれ、関数型プログラミングなどでは重要な概念です。(ただし、完全に不変オブジェクトであるにはフィールドの参照値が指す先のオブジェクトも不変オブジェクトでなくてはなりません)

不変オブジェクトは後から変更できないために一見不便に見えますが、マルチスレッドやテストでその利便性がわかります。不変であるため、そのオブジェクトを扱うメソッドは(引数のオブジェクトその内部で使うグローバルオブジェクトについて副作用が無ければ)何もしなくてもスレッドセーフになり、面倒な同期やロックを考える必要が無くなります。また、状態の変化が無いため、状態の変化に対するテストが不要になります。状態が変わることによって振る舞いが変わるようなオブジェクトのテストはかなり面倒ですが、それをまるまる省けると言うことです。

では、名前を変更したいとなったらどうするかというと、新たなオブジェクトを作ることになります。

Java

1public Person changeName(Label name) { 2 return new Person(name, this.address); 3}

前のオブジェクトはそのままですので、メモリ消費量が多くなりますが、逆に言うと前のオブジェクトをそのまま使い続けられと言う利点が生まれます。Javaでも新たに追加されたクラス(java.nio.file.Pathやjava.time.LocalDateTime等)は不変オブジェクトとして作られる場合が多いようです。

ただし、今回のコードのPersonは必ずしも不変オブジェクトであるとは限らず、例え不変オブジェクトであっても、スレッドセーフと限りません。

まず、Labelが不変オブジェクトと限らないと言うことです。Labelの_labelにfinalが無いからではありません。現在の実装では_labelを生成後に変更することはできないため、その意味では不変です。また、String自体は不変オブジェクトです。しかし、Labelのサブクラスも同様に不変である問い保証はありません。PersonはLablelおよびそのサブクラスを受け付けるため、Personの不変性が崩れる可能性があります。これを完全に防ぐにはLabel自身にfinalをつけるか、Labelそのもの以外は例外で弾くかしないと無理でしょう。

次にスレッドセーフかですが、副作用を伴うSystem.out.println()を使っているため、その条件を満たしていません。自動的にスレッドセーフと言えるのは不変オブジェクト以外を扱わず、一切の副作用がない場合です。これを自動的に検知するような仕組みはJava自体にはありません。

最後に、上を修正してPersonを不変オブジェクトにしても、そのサブクラスは同じく不変オブジェクトであるは限りません。Person型とされる物は全て不変と扱ってはいけないと言うことです。これを防ぐには同じくPersonクラス自体をfinalにする必要があるでしょう。その意味では、少し片手落ちのような気もします。

このように、全てがfinalなフィールドであることは不変オブジェクトの十分条件でも必要条件でもありません。ただ、もし、不変オブジェクトであれば、全てのフィールドをfinalにしてもコンパイルエラーが起きないはずです。間違って不変オブジェクトでは無くなってしまっていることを検知するためにfinalに設定することは無駄ではありません。

②PersonクラスでLabel型の_nameと_addressを宣言していますが、継承等を行っていないのに宣言して、コンパイルエラーが出ない仕組みがよくわかっていません。PersonクラスでLabel型は使えるものでしょうか?
また、継承等を行わなくてもいいのでしょうか?

継承するのか、それとも持つだけなのかは重要なテーマです。それぞれis_ahas_aと呼ばれています。継承すればStringとしても扱えるというのは利点ですが、Stringとしても扱えてしまうと言うのは欠点でもあります。あまり、それ自身として扱って欲しくない場合は、has_aな関係を持った方が良い場合があります。

しかし、Javaの場合は継承禁止というのがあります。Stringはfinalなクラスであるため継承できません。なので、has_aの関係、つまりフィールドに持つだけという関係にするしかありません。それでも、なるべくStringのように振る舞いたい場合は、必要なメソッドを一つ一つ委譲することになります(Lombokの@Delegateを使うという手もありますが)。

投稿2016/12/20 22:31

編集2016/12/21 21:16
raccy

総合スコア21735

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

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

退会済みユーザー

退会済みユーザー

2016/12/20 23:48

Private でfinal ではない変数はクラスないならどこでも変更可能
退会済みユーザー

退会済みユーザー

2016/12/20 23:51 編集

あと不変なのは入れ物だけです。中身は自由に入れ換え可能なのでスレッドセーフではないです
KSwordOfHaste

2016/12/22 00:30 編集

raccyさんはPersonのインスタンスが不変になるといっているだけであってそれはそのとおりだと思います。Cのインスタンスがimmutableであるということには「Cが保持するX型のフィールドC.xの中身が不変かどうか」には直接関係しない概念だと思います。Cが保持するフィールドの中身が不変かどうかはCとXの設計によるはずです。Xのインスタンスの状態の変化がCの不変性に影響するならCはそれに配慮した実装を行うはずです。例えばX.clone()によるコピーの結果をC.xへ設定しておくなど。しかしraccyさんの回答の文脈をみるにそういった端々の点までに言及してはおらず単にfinalでCのフィールドを変更しないことを保証できる意味について論じているだけなので不自然には感じませんでした。あまり厳密に書きすぎるとかえってわけがわからくなることもあるし回答は難しいですね・・・
退会済みユーザー

退会済みユーザー

2016/12/21 18:06

ご回答ありがとうございます。 追加で1点聞きたいことがあります。 PersonクラスでLabel型の_nameを定義しているのですが、イメージがわきません。 String型との違いがあるのかと、Labelクラスで_nameは定義されていないのに使えるのかを教えていただけると助かります。
swordone

2016/12/21 18:29

「_nameという名前のLabel型変数」という意味であって、「Label型変数の_name」ではありません。 Labelにしている意味は正直わかりません。「Labelをfinalで宣言しても、その中身は書き換えられるよー」という実験コードなのではないでしょうか?
raccy

2016/12/21 21:12

色々追記しました。 > final ではない変数は ??? Personの話ではなくて? > あと不変なのは入れ物だけです。 どこの話ですか?Labelの話ですか? > immutableの定義というか概念はスレッドセーフであることが一つの条件 immutableだからこそ、そのようなオブジェクトを扱うメソッドは必ずスレッドセーフになるはずなんですが…。不変オブジェクトでは無いものを内部で扱っているメソッドはスレッドセーフとは限りませんが、そのようなメソッドがある場合は不変オブジェクトではないというのは私にはよくわかりません。(ただ、不変オブジェクトのメソッドで不変オブジェクト以外を扱うコードを書くこと自体が私にはもっと意味がわからないことなのですが) > PersonクラスでLabel型の_nameを定義しているのですが 意味を持たせるためにStringやintなどを直接使わずクラスを使うという文化があるらしいです。 http://d.hatena.ne.jp/asakichy/20090615/1245025255 > Labelをfinalで宣言しても、その中身は書き換えられるよー」 今のコードだとLabel自体は書き換えるためのメソッドが無いんですよね。サブクラスだといけるとかの説明するためのコードかも知れません。
guest1213

2016/12/22 01:39

raccyさんへ > 全てがfinalなフィールドのオブジェクトは不変(immutable)オブジェクトともいわれ この文がちょっと引っかかってコメントしただけです。その後、 > 全てがfinalなフィールドであることは不変オブジェクトの十分条件でも必要条件でもありません。 とおっしゃってるので、なんでしょう、引っかかった部分はニュアンスを私が理解できていなかったのでしょうか。 > immutableだからこそ、そのようなオブジェクトを扱うメソッドは必ずスレッドセーフになるはずなんですが…。 「不変なオブジェクトを扱うメソッド」の話でしょうか?私は不変オブジェクト自体がスレッドセーフであることを言いたかったのですが…。上のコメントを「不変だけどスレッドセーフではない(こともある)」と捉えてしまったので(今読むとそんなことは言ってない気もしますが)、不変オブジェクトならばスレッドセーフであるという根拠はなにかないかなとついコメントしてしまいました。 私はJavaにおいては不変オブジェクトならばスレッドセーフなんだとずっと思ってましたが > 例え不変オブジェクトであっても、スレッドセーフと限りません。 とおっしゃってますが、こういうこともあるのですか?考え得る状況としては、オブジェクト自身の状態は不変でも副作用のあるメソッドをそのオブジェクトが持つ場合かと思いますが、そのオブジェクトは「不変オブジェクト」といえるのでしょうか? 「不変オブジェクトのみを扱うメソッドはスレッドセーフである」(つまり不変オブジェクトもスレッドセーフである)とおっしゃっているのに、コメントで > 不変オブジェクトでは無いものを内部で扱っているメソッドはスレッドセーフとは限りませんが、そのようなメソッドがある場合は不変オブジェクトではないというのは私にはよくわかりません。 というように「スレッドセーフではないメソッドを持っていても不変オブジェクトである場合がある」とおっしゃっていたり、矛盾しているようでどうも釈然としませんでした。 最後の方はご回答とコメントへの感想程度に思ってください。正確に色々説明すると長くなってしまうでしょうから。
raccy

2016/12/22 11:57 編集

そのオブジェクト自体が不変だからといって、そのオブジェクトの全てのメソッドがスレッドセーフになるとは限らないと言うことです。どうやら、不変オブジェクトの定義自体が違っているようですので、議論することに意味が無いと思います。
guest

0

①Personクラスの_nameと_addressにfinalをつける必要がありますか?

finalをつけることにより、初期化した後の上書きを防止します。このPersonクラスの場合、コンストラクタで与えられたLabelで初期化した後に、_name = new Label("適当な文字列");のような変更ができなくなり、安全になります。

②PersonクラスでLabel型の_nameと_addressを宣言していますが、継承等を行っていないのに宣言して、コンパイルエラーが出ない仕組みがよくわかっていません。PersonクラスでLabel型は使えるものでしょうか?

また、継承等を行わなくてもいいのでしょうか?

それを言い出したら、LabelクラスでStringというクラスが使えなくなります。Javaの標準ライブラリにStringという「便利な道具」があるからそれをそのまま使っているのと同様、自分で作ったLabelという「便利な道具」を使っているだけにすぎません。
この道具を自分で改造して機能を増やしたり変えたりして使おうというのが継承です。

投稿2016/12/20 17:49

swordone

総合スコア20651

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

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

0

一部だけ補足的なコメントをさせてください。

言葉ではピンとこない場合には図で話を考えたり議論するとわかりやすくなるかも知れません。

図では箱がインスタンスで、箱の左上の名前(Person, Label)がインスタンスのクラス名です。
"太郎"などもインスタンスなので本当なら箱で囲むべきものです(面倒なので単に"太郎"などと書いてます)。

メモリー上の実体のイメージ(継承関係を示すようものではないです) Person-----+ +--> Label ----+ |_name --------+ |_label ----> "太郎" | | +---------+ |_address -----+ +----------+ +--> Label ----+ |_label ----> "横浜市" +---------+ //class A extends Bのように継承だと(あまり厳密じゃない点もありそうだけど)埋まっているイメージ A----------+ |B--------+| ||_b1 || |+--------+| |_a1 | +----------+

②...継承等を行っていないのに...PersonクラスでLabel型は使えるか?

また、継承等を行わなくてもいいのでしょうか?

ご質問の内容やコメントを拝見すると図とは違ったイメージをお持ちなのではないかと感じました。ひょっとしたらPersonの内側に(継承してないのに)Label(の属性など)が含まれているイメージをお持ちのために混乱されたのではないかと想像します。raccyさんがおっしゃるようにPersonにLabelが埋まっているあるいは継承している(is_a)ではなくPerson has a Label (via _name)といった感じです。

Label型とすることとString型との違いがあるのか

一般論としては色々な設計上の違いについて考えられるかも知れませんが、このコードそのものについては素朴にみて取れることはStringにはないLabel#displayがあるのでDRY原則的な意義がある点でしょうか。Labelの代わりにStringを用いた際に以下のようになるので。

java

1class Person { 2 String _name; 3 String _address; 4 ... 5 public void display() { 6 if (_name != null) { 7 System.out.println("display:" + _name); 8 <-------A--------> <---B----> 9 A:出力先をSystem.out以外にしたくなったとき複数個所に修正が必要 10 B:プリフィックスであるBの内容を変更した際に複数箇所に修正が必要 11 } 12 if (_address != null){ 13 System.out.println("display:" + _address); 14 } 15 } 16} 17// 本題からはずれますがどうせならnullかどうかのif文もLabel(あるいはOptionalのようなもので)省きたいですね

投稿2016/12/22 02:44

KSwordOfHaste

総合スコア18394

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問