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

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

ただいまの
回答率

90.50%

  • Java

    15842questions

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

継承時のthisの働き

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,380

nom_0124

score 15

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

class Super{

    private String name;

    publuc Super(String name){
        this.name=name;
    }

    public String getName(){
        return this.name;
    }
}

class Sub extends Super{

    public Sub(String name){
        super(name);
    }
}

public class Main{

    public static void main(String[] args){

       Sub sub=new Sub("Java");

       System.out.println
               (sub.getName());
    }
}


このコードを実行すると、「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:皆さんの回答は大変ためになりました。
ただ僕にとっては難しい内容で、考えるのはまだ早いのかな。と思いました。
なので今の段階では皆さんの回答を参考にし、なんとなくの理解に留めておきます。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • mts10806

    2017/07/07 13:19

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

    キャンセル

  • mattn

    2017/07/07 14:40

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

    キャンセル

回答 4

checkベストアンサー

+5

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

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

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

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

public String getName() {
    System.out.println(this);
    return this.name;
}

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

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

class Sub {
    private Super super;

    public Sub(String name) {
        super = new Super(name);
    }

    public String getName() {
        return super.getName();
    }
}

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 クラスにコピーされる様に見えるかもしれませんが、実際は独立したクラスとなっていて個々にアクセス可否できる関係にあります。

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

class Super{
    protected String name;

    public Super(String name) {
        this.name = name;
    }

    public String getName() throws Exception {
        if (true) throw new Exception();
        return this.name;
    }
}

class Sub extends Super {
    public Sub(String name) {
        super(name);
    }
}

public class Main{
    public static void main(String[] args) throws Exception {
        Sub sub = new Sub("Java");
        System.out.println(sub.getName());
    }
}

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

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 16:44

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

    キャンセル

  • 2017/07/10 13:24

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

    キャンセル

+4

継承はよく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/10 13:26

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

    キャンセル

+1

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

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

とか

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

とか。

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

という受け売りでした。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/10 13:25

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

    キャンセル

+1

つまり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 14:49

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

    キャンセル

  • 2017/07/07 15:08

    はい、あくまでイメージです。
    実装として考えると、継承とはかけ離れたコードです。

    継承することでSuperクラスのプライベート変数がアクセス可能と思っているようなので、
    アクセスが不可能なことをイメージしやすいかなと思いまして。

    キャンセル

  • 2017/07/10 13:25

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

    キャンセル

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

  • Java

    15842questions

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