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

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

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

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

Q&A

解決済

4回答

8486閲覧

継承時のthisの働き

nom_0124

総合スコア23

Java

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

2グッド

1クリップ

投稿2017/07/07 04:18

編集2017/07/10 04:24

こんにちは。現在Javaについて勉強しています。
今回はその継承に関して、調べてもよくわからない点があったので質問させてください。
見て頂きたいのは以下のコードです。

Java

1class Super{ 2 3 private String name; 4 5 publuc Super(String name){ 6 this.name=name; 7 } 8 9 public String getName(){ 10 return this.name; 11 } 12} 13 14class Sub extends Super{ 15 16 public Sub(String name){ 17 super(name); 18 } 19} 20 21public class Main{ 22 23 public static void main(String[] args){ 24 25 Sub sub=new Sub("Java"); 26 27 System.out.println 28 (sub.getName()); 29 } 30}

このコードを実行すると、「Java」と表示されます。
わからないのはsub.getName()です。
super(name)はSuperクラスのコンストラクタを呼び出しているので、結果Superクラスの変数nameに実引数の"Java"が代入されているとわかります。
しかしsub.getName()という処理は、Superクラスから継承されたSubクラスのgetNameメソッドを呼び出すものです。
つまりSubクラスのgetNameメソッドが実行されて返すのはSubクラスの変数name(定義、継承していませんが)で、Superクラスの変数nameではないはずです。
何故ならthisとは「自身のインスタンス」という意味で、この場合Subクラスのインスタンスを示すからです。
それに子クラスからthisでアクセス出来たら他クラスからのアクセスを制限するprivateの意味が無いじゃないですか?
よって本来はSubインスタンスの変数nameが見つからない旨のコンパイルエラーが起きると思うんです。
なのに正常に動作する。僕にとっては異常なのですが。
そこで考えられる可能性としては
継承という行為は継承元から継承先に利用可能箇所を切り取って貼り付ける、みたいなものではなく、全てを内包するという意味。もしくはその上で利用可能箇所を利用可能にすることを継承と言うのかもしれません。
つまりどちらにしろこの場合Superクラスの全てのメンバ(private変数name,getNameメソッド)をSubクラスが保有し、そこから利用できるメンバ(getNameメソッド)だけを利用出来る。
そしてそれらのメンバはSubクラスのメンバではあるが、厳密にはSubクラスに含まれるSuperクラスのメンバであって、Mainクラスからsub.getName()で呼び出した際の処理も、スーパークラス内で行われることに他ならない。
だとしたら一応筋は通る気がするのですが、初心者故間違っていないか心配です。
どなたか僕の悩みを解決していただけないでしょうか?
よろしくお願いします。

追記

7/7:プログラムコードを```で括ってないことについてご指摘くださりありがとうございます。
この件は僕が無知であり、また配慮が足りていない証拠です。
まことに申し訳ありませんでした。

7/10:皆さんの回答は大変ためになりました。
ただ僕にとっては難しい内容で、考えるのはまだ早いのかな。と思いました。
なので今の段階では皆さんの回答を参考にし、なんとなくの理解に留めておきます。

mr_vinylover👍を押しています

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

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

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

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

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

m.ts10806

2017/07/07 04:19

プログラムコードは```で囲ってください。(わからなければ質問編集画面でコード部分を選択し<code>ボタンを押してください)
mattn

2017/07/07 05:40

コードブロックは今からでも直せるので直しましょう。
guest

回答4

0

ベストアンサー

Java での継承とは何かについてですが KSwordOfHaste さんが書いて頂いた様に is a の関係です。

言葉だけでは難しいかもしれませんが、誤解を恐れず言うと「成り切り」の関係です。

Sub クラスは extends で指定した Super クラスに「成りきる」事になります。getName の様に Super クラスに追加されたメソッドを Main クラスから実行するとランタイムは「Sub クラスが Super クラスに成りきったつもりで Super クラスの getName を実行」します。

これは例えば Super クラスの getName を以下の様に書きかえて確認頂くと分かるかもしれません。

java

1public String getName() { 2 System.out.println(this); 3 return this.name; 4}

Super クラスの中に書いたコードなのに Sub と表示されますよね。これが意味するのは、あくまで Sub クラスの中にいる Super クラスの実体を使って getName を呼び出している訳ではないという事です。

ちなみに、Sub の中にいる Super の実体というポリモーフィズムも一般的には「継承」と称される事がありますが、こちらは「委譲」と呼ばれます。

java

1class Sub { 2 private Super super; 3 4 public Sub(String name) { 5 super = new Super(name); 6 } 7 8 public String getName() { 9 return super.getName(); 10 } 11}

szk. さんが書いてくれたコード、これが委譲です。委譲は先に説明した様に「Super のメソッドを Sub が成りきって実行」しているのでなく「Sub が保持する Super のインスタンスに対して getName を呼び出す様にお願いしている」わけです。ここが継承と委譲が区別される所です。

フィールド参照も「成り切り」です。Sub クラスのどこかで this.name を参照すると

nameはSuperでprivateアクセスされます

というエラーが出ます。これは「Sub クラスが Super クラスに成りきったつもりで Super クラスの name を参照した」が、Super クラスが private を指定した為にアクセス出来なかった事を意味します。Sub クラスが Super クラスから name をコピーされた訳ではないという事です。これは protected で許可出来る事はご存じかと思います。

一件、extends で継承すると Super クラスの持っている実装が全て Sub クラスにコピーされる様に見えるかもしれませんが、実際は独立したクラスとなっていて個々にアクセス可否できる関係にあります。

これも以下のソースを実行してみると分かると思います。

java

1class Super{ 2 protected String name; 3 4 public Super(String name) { 5 this.name = name; 6 } 7 8 public String getName() throws Exception { 9 if (true) throw new Exception(); 10 return this.name; 11 } 12} 13 14class Sub extends Super { 15 public Sub(String name) { 16 super(name); 17 } 18} 19 20public class Main{ 21 public static void main(String[] args) throws Exception { 22 Sub sub = new Sub("Java"); 23 System.out.println(sub.getName()); 24 } 25}

例外を吐いて死ぬコードですが、このスタックトレースを見て下さい。

Exception in thread "main" java.lang.Exception at Super.getName(Main.java:9) at Main.main(Main.java:23)

Main クラスから Sub を経由せず直接 Super クラスの getName が実行されている事になります。

この様に、継承とは「成り切り」で考えると分かりやすいかと思います。

それに子クラスからthisでアクセス出来たら他クラスからのアクセスを制限するprivateの意味が無いじゃないですか?よって本来はSubインスタンスの変数nameが見つからない旨のコンパイルエラーが起きると思うんです。

実際に Sub クラスには name は見つからないのですが、フィールドやメソッドの区別なくそれを見える様にするのが継承になります。そしてアクセス可否によりエラーとなる訳です。

そしてそれらのメンバはSubクラスのメンバではあるが、厳密にはSubクラスに含まれるSuperクラスのメンバであって、Mainクラスからsub.getName()で呼び出した際の処理も、スーパークラス内で行われることに他ならない。

はい。間違いは無いと思いますが上記の「成りきり」で考えて頂くとその他についても分かりやすいかと思います。

投稿2017/07/07 06:57

mattn

総合スコア5030

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

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

KSwordOfHaste

2017/07/07 07:44

「メソッドが全部コピーされているかのように捉えてもよい」という自分の説明はスタックトレースを見たとき「だってSuper.getNameが呼ばれてるんですけど!」と混乱させる可能性があるかも知れず、その点「成り切り」というのはうまい表現だなぁと思いました~
nom_0124

2017/07/10 04:24

mattn様 継承の際の動きが複数の例示を以て詳しく説明されていたという点に感謝し、ベストアンサーを差し上げたいと思います。 回答ありがとうございました。
guest

0

継承はよくis a関係と呼ばれます。

Sub is a Superを日本語で表現するなら「SubはSuperです」となります。
もう少し言えば「Subのインスタンスは同時にSuperのインスタンス」でもあります。
さて、SubがSuperのインスタンスであるための条件はなんでしょうか?基本的にそれは「Superのインスタンスに対して行えることはSubのインスタンスに対してもまた行える」ということを意味すると考えてよいと思います。

継承という行為は継承元から継承先に利用可能箇所を切り取って貼り付ける、みたいなものではなく、全てを内包するという意味。

そう考えてよいと思います。

そしてそれらのメンバはSubクラスのメンバではあるが、厳密にはSubクラスに含まれるSuperクラスのメンバであって、Mainクラスからsub.getName()で呼び出した際の処理も、スーパークラス内で行われることに他ならない。

スーパークラス内で行われていることに他ならないという表現は若干誤謬を含む可能性があると思います。sub.getName()はあくまでSubのインスタンス内で行われており、Superから引き継いだSubのgetNameが動いていると捉えても一向にかまわないです。(Javaの実装上はメソッドの実体はSuperにしかないですが、Super内部でメソッドが動いているというような捉え方よりはむしろ、SubのためにコピーされたgetNameがSubのインスタンスの元で動いていると捉えた方が本質を捉えやすい気がします。)

投稿2017/07/07 04:52

編集2017/07/07 05:49
KSwordOfHaste

総合スコア18394

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

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

nom_0124

2017/07/10 04:26

KSwordOfHaste 説明が丁寧で、すごく分かりやすかったです。 ベストアンサーを差し上げられないことが無念です。申し訳ありません。 回答ありがとうございました。
guest

0

つまりSubクラスのgetNameメソッドが

~中略
のコンパイルエラーが起きると思うんです。

だいたい間違ってます。

つまりどちらにしろこの場合Superクラスの全てのメンバ

~中略
スーパークラス内で行われることに他ならない。

7割くらいはあってると思います。

間違いを正すと
例えばこんな風に書けば、Superのプライベート変数は隠蔽されているように感じませんか?

class Sub{ private Super super; public Sub(String name){ super = new Super(name); } public String getName(){ return super.getName(); } }

実装しちゃうと、継承ではなくWrapperとかって言うはずなんですが、
あくまでボクの中での、継承されているインスタンスの内部のイメージです。

投稿2017/07/07 05:06

szk.

総合スコア1400

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

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

mattn

2017/07/07 05:49

説明の為に書いて頂いたコードだと思うのですが、これは「委譲」というまた異なるポリモーフィズムなのでちょっ回答としてはズレてしまったかもしれないです。
szk.

2017/07/07 06:08

はい、あくまでイメージです。 実装として考えると、継承とはかけ離れたコードです。 継承することでSuperクラスのプライベート変数がアクセス可能と思っているようなので、 アクセスが不可能なことをイメージしやすいかなと思いまして。
nom_0124

2017/07/10 04:25

szk.様 「イメージしやすい説明」を配慮してくださり、ありがとうございます。 僕みたいな初心者にとってわかりやすいことを心掛けるのは難しいことだったと思います。申し訳ないです。 そして回答ありがとうございました。
guest

0

学生時代、人間とか車でよく例えられました。

人(スーパークラス)
男性(人クラスのサブクラス)
女性(人クラスのサブクラス)

とか

車(スーパークラス)
トラック(車クラスのサブクラス)
バス(車クラスのサブクラス)

とか。

広瀬すずは女性クラスのインスタンスであり人クラスのインスタンスでもあるわけです。
人クラスに歩く関数があるので、女性クラスのインスタンスも男性クラスのインスタンスも歩く関数を定義しなくても利用できるのです。

という受け売りでした。

投稿2017/07/07 05:02

Clor

総合スコア883

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

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

nom_0124

2017/07/10 04:25

Clor様 理解が深まりました。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問