【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】

javaにおけるprivateメンバの動きについて伺いたいことがございます。

基本的にスーパークラスのprivateなメンバへはメソッド・フィールドともにサブクラスからは
アクセスできないとのことですが、

例えば、以下のようなコードがあるとします。
//スーパークラスの定義
class SuperClass{

private String privateString = "スーパークラスのプライベートメンバ";

public void getter(){
System.out.println(this.privateString);
}
}
//サブクラス
class SubClass extends SuperClass{

}
//実行クラス
class RunClass {

static public void main (String args []){

SubClass subObj;

subObj = new SubClass();

subObj.getter();
}
}
上記のようなコードの場合サブクラスのインスタンスであるsubObjが
スーパークラスのpublicなメソッドを通じてサブクラスからスーパークラスのprivateメンバへアクセスが
できてしまっています。
これは、スーパークラスのprivateが隠蔽されていないのではないでしょうか?

果たして、このときいったサブクラスのインスタンスのsubObjはどういった動きをしているのでしょうか?

A 回答 (7件)

>つまり、スーパークラスのメンバをオーバーライドしてもスーパークラスのメソッドは


>保持されたまま、単純にサブクラスでオーバーライドしたメソッドの方がスーパークラスより
>優先して呼び出されるというような意味合いでしょうか?

「優先」とはちょっと違ってて、基本的に SuperClass のメソッドは 
Subclass に同名のメソッドを作ると「置き換えられ」ます。これが override です。

C++ とかだとこのへんの実装が丸見えなので、具体的な実装方法を覗いて見たい時は
JAVA より C++ が適しているかも。

override は OOP の「多態性」(ポリフォーニズム)を実現するための
ものなので、概念的な部分はこのキーワードで調べてみたらよいでしょう。

SubClass型のインスタンス(変数の型ではなくて、newした時の型)が
呼び出すメソッドは置き換えられた方のメソッドですが、
型を指定して呼び出す方法があり、それが super です。この場合、
指定された型に結びついたメソッドが呼び出されます。
    • good
    • 0

だいたい正しいのですが、



>(5)スーパークラスで定義していたメソッドをサブクラスでオーバーライドすることによって
>スーパークラスでの定義を破棄し、サブクラスで定義したことになる。
>そのため、オーバーライドしたメソッドからはスーパークラスのprivateメンバへはアクセス
>することができなくなる。

はちょっとだけ違います。スーパークラスでのメソッドは破棄されずに残ってます。
サブクラスから override 前のスーパークラスのメソッドを呼び出すことが可能です。

これを使って override 前の処理に新たな処理を加えたりすることができます。

この回答への補足

>スーパークラスでのメソッドは破棄されずに残ってます。
サブクラスから override 前のスーパークラスのメソッドを呼び出すことが可能です。

superキーワードによるスーパークラスのメンバの呼び出しができるということですね。

つまり、スーパークラスのメンバをオーバーライドしてもスーパークラスのメソッドは
保持されたまま、単純にサブクラスでオーバーライドしたメソッドの方がスーパークラスより
優先して呼び出されるというような意味合いでしょうか?

補足日時:2012/08/09 07:40
    • good
    • 0

No3 です



まだ、発想が逆です。


スーパークラスを 変える際にどういう仕組みだったら変えやすいか? 


private な 変数は どう変えようが 影響しない。 


アクセス云々にこだわるより、もっと大切なことがあるのです。リファクタリングやデザインパターンとか見るべきです。 テンプレートパターンとか FactoryMethod や Abstrct Factory とか

この回答への補足

うーん。
私が知るべきはデザインパターンなど上の層ではなくもっと基本のクラスを継承することによるサブクラスの詳細な動きなのではとおもっています。例えば
他の方が答えてくださっているような、サブクラスからなぜスーパークラスにアクセスできるのかとか、privateなメンバは継承されるのか否かとか・・・・・。
pivateなメンバに関しては諸々の文献で異なった見解を多数見受けられます。
ある書籍はprivateなメンバは継承できないとか、またある書籍はprivateなメンバももちろん継承されるとかかれていたり・・・そのようなあやふやな箇所があるので当初の質問をしています。

補足日時:2012/08/09 00:00
    • good
    • 0

(a)あるクラスのprivateなメンバへは,別クラスのメソッドからは直接アクセスできない。


(b)別クラスのメソッドから あるクラスのpublicなメソッドを呼び出し,それを通じてあるクラスのprivateなメンバにアクセスすることならできる。
それが情報隠蔽です。
http://oshiete.goo.ne.jp/qa/7340001.html の私の回答ANo.2

--------
スーパークラスの getter() が
サブクラスにおいて同名メソッド getter() でオーバライドされているなら,
subObj.getter() という記述は,サブクラスで定義されたgetter()を呼び出します。
前述(a)のとおり,このメソッドからはスーパークラスのprivateStringには直接アクセスできません。
(ちなみに前述(b)のとおり,サブクラスのgetter()からスーパークラスのgetter()を呼び出してスーパークラスのprivateStringにアクセスすることはできます,念のため)

質問文に掲載されたコードでは,
サブクラスに同名メソッドgetter()のオーバライド定義がありませんから,
subObj.getter() という記述は,継承元のスーパークラスのgetter()を呼び出します。
getter()もprivateStringも同一クラスに存在しますから,このメソッドからprivateStringに直接アクセスすることができます。

この回答への補足

>(b)別クラスのメソッドから あるクラスのpublicなメソッドを呼び出し,それを通じてあるクラスのprivateなメンバにアクセスすることならできる。
それが情報隠蔽です。


なるほど、privateの仕様は定義した当該のクラス内部からのみアクセス可能という
重要な部分をpublicやprotectedを使用してprivateなメンバへアクセスできる
メソッドを定義してそれをサブクラスにコールさせることで間接的?にスーパークラス内部からアクセスしたように見せかけているようなものですね。

さらに
サブクラスでそのような間接的にアクセスさせるメソッドをオーバーライドさせてしまうと
オーバーライドしたメソッドはスーパークラスからは削除され(superというキーワードがありますが・・・・)サブクラスで初めて定義されたものとみなされ
スーパークラス内で定義されたメソッドからならアクセス可能という条件から外れてしまうため
スーパークラスのprivateなメンバへアクセスできなくなってしまうというわけですね。

#2の人への返信にも、付加したのですが
自分なりにまとめてみました。
おかしなところなどありましたら
是非、お聞かせいただきたいです。
https://twitter.com/1000_1000_/status/2331771146 …
上が醜ければ
https://p.twimg.com/Azxo60QCcAAxgEE.png:large
を参照ください。

補足日時:2012/08/08 21:48
    • good
    • 0

発想が違うと思います。



オブジェクト指向は スーパークラスと派生クラスの結合の弱さを大事にします。つまり、発生クラスはそのままで
スーパークラスを変更する、その際、private な変数は一切消えて作り替えても、支障をきたさない。そうなってなければ、スーパークラスは変更できないわけです。

プログラムを変更する、そのことから 考えてください。

この回答への補足

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

つまりは、基本的にprivateなメンバがあるクラスに関しては
extendsキーワードでクラス拡張するべきではないということでしょうか?

あるいは逆の意味・・・
クラス拡張を前提としたクラスをつくるのであれば
privateメンバを利用するべきではないということでしょうか?

では、質問のコードにもあるようにサブクラスからスーパークラスの
publicやprotectedメソッドを通してスーパークラスのprivateへアクセスできるのは
オブジェクト指向上好ましくないということですか?

補足日時:2012/08/08 21:55
    • good
    • 0

>継承されたメソッド getter()はSubClass内のprivateStringというフィールドを


>見に行かなければいけないのではないでしょうか?

そうですよ。privateStringも継承されます。インスタンスはあくまで
SubClass型で SuperClass型ではありません。

そして SuperClass で private で宣言されたフィールドは
SuperClass で宣言されたメソッドからしかアクセスできません。

そのようにコンパイラがアクセスを制御するからです。

それから「隠蔽」というのは直接アクセスを禁ずるだけで間接的なアクセスはOKです。
もしそれさえも禁じてしまえば、親クラスの存在意義はなくなります。

この回答への補足

ご回答まことにありがとうございます。
個人的に、ちょっと考え違いをおこしていたようです。
そもそも継承とは、protectedおよびpublicを継承するためのものではなくて
classそのものを定義するサブクラス内に包含するといういみでよいでしょうか?
ちょっと自分なりに、まとめてみたのでごらんいただけますか?

https://twitter.com/1000_1000_/status/2331771146 …
上が醜ければ
https://p.twimg.com/Azxo60QCcAAxgEE.png:large
を参照ください。

要点としては、
(1)クラスの継承(拡張)とはクラスそのものをサブクラスの中に包含すること
かつ、privateなメンバもそれに含まれる。

(2)スーパークラスのprivateなメンバにはスーパークラスのメソッドからしか
アクセスできない。

(3)そして、スーパークラスをextendsしたサブクラスはそのサブクラス内に包含している
スーパークラスのprotectedおよびpublicにしかアクセスすることはできない。

(4)スーパークラスのprivateは当該のスーパークラスからしかアクセスすることができないが
スーパークラス内で定義したprotectedやpublicメソッドをサブクラスがコールしアクセスすることで
間接的にスーパークラス内からprivateメンバへアクセスしたことと同義となる。

(5)スーパークラスで定義していたメソッドをサブクラスでオーバーライドすることによって
スーパークラスでの定義を破棄し、サブクラスで定義したことになる。
そのため、オーバーライドしたメソッドからはスーパークラスのprivateメンバへはアクセス
することができなくなる。

というところでしょうか?
アップした図および、上記項目に解釈の間違いがあれば
是非、ご指摘いただいたいです。
よろしくお願いします。

補足日時:2012/08/08 21:37
    • good
    • 0

privateの働きはここではsubObj.privateStringのように直接アクセスするのを禁止することや、


サブクラス内でthis.privateStringのようにアクセスするのを禁止する事です。

つまり、privateという変数はサブクラスを含む外部から、直接その変数名を指定してアクセスされることを禁止するだけです。
そしてあくまで、privateStringは外部からは見えていないので隠蔽されています。

この回答への補足

なるほど、わかりました。
しかしながら、
スーパークラスのメソッドgetter()は継承されてSubClassからアクセスしているのですよね?

そう解釈するのであれば継承されたメソッド getter()はSubClass内のprivateStringというフィールドを見に行かなければいけないのではないでしょうか?

なぜ、スーパークラスのprivateなメンバであるprivateStringという変数を見に行くのでしょうか?

補足日時:2012/08/08 00:28
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!