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

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

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

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

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

4回答

1161閲覧

java アクセス修飾子を強める方向でオーバーライドできないのはなぜでしょうか?

k.fujisawa

総合スコア29

Java

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

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

1クリップ

投稿2024/03/05 13:34

アクセス修飾子を強める方向でオーバーライドできたとしても、
アクセス修飾子はインスタンスでなく、クラスの型に依存するため、
オブジェクト指向のポリモーフィズムは破綻しないと考えます。

どういった設計思想に基づき、アクセス修飾子が強制されているのでしょうか?
強制しなくても、リスコフの置換原則は満たされませんか?
理解できなかったので、教えてください。

<参考ソース>

//アクセス修飾子はクラスの型に依存する
//public ArrayList.clone
new java.util.ArrayList().clone();
//protected Object.clone
//Compile Error
//The method clone() from the type Object is not visible
//((Object)new java.util.ArrayList()).clone();

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

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

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

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

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

guest

回答4

0

ベストアンサー

アクセス修飾子は強制ではありませんが、他回答でコメントされている意図に沿い、広い方向へのオーバーライドを考えてみましょう

ObjectArrayListの例の通り、可視性はprotected->publicと緩和されますが、これはアクセスできる範囲を広げます

オーバーライドされたメソッドは、派生クラスの実装で上書きされますが、これは表現を変えれば派生クラスのインスタンスへのアクセスを強制すると言うことです
オーバーライドの目的は基底クラスのメソッドを無視することなので、派生クラスのメソッドはどこからアクセスされても不都合がないと言えます

一方で派生クラスへのアクセスを強制する目的で捉えた場合、基底クラスのメソッドに簡単にアクセスされては困ります

public->protectedの方向で考えた場合、基底クラスへのアクセスはどこからでも可能ですが、派生クラスへのアクセスはpublicよりも狭い範囲になります

オーバーライドはアクセス先を強制はしますが、一方で基底クラスへのアクセスを禁止しません
アップキャストを行えば容易に基底クラスのメソッドを宣言できます

これはメソッドへのアクセスが派生と基底で別れて管理されていることの証です
仮に基底クラスの方がアクセス範囲が広ければ、その範囲の網羅する場所では基底クラスのメソッドが優先され、逆に限定的な範囲では派生クラスのメソッドが選択されることになります

public->protectedの場合はオーバーライドが派生クラス自体とそれを継承する子、あるいはパッケージ内のクラスに限定されるでしょう
これが置換原則を保っているか?と問われれば「アクセス元の場所によって実装の適用元が変化する」という意味で破綻しています

とりわけ抽象メソッドとなれば実装を持たないので、そのメソッドは使えなくなってしまいます
オーバーライドは、派生クラスのメソッドが基底クラスのメソッドのアクセス範囲をカバーする責務があるということです

ちなみにこのような狭い方向へのオーバーライドをコンパイラは「弱いアクセス権限」と呼んでいます

投稿2024/03/06 03:30

TokoToko123

総合スコア26

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

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

k.fujisawa

2024/03/06 05:04

うわ、弱いアクセス権限と呼ばれてるんですね。 あれ?privateは弱いアクセス権限ですか? そして強いアクセス制限ですか? "狭い方向へのオーバーライド"を指す言葉として"弱いアクセス権限"が使われています?
TokoToko123

2024/03/06 07:35

>privateは弱い権限か そういうことになります 狭い=弱い、広い=強い、という関係でしょう なので権限が最も強い(制約が最も弱い)レベルはpublicです >狭い方向へのオーバーライドを指す言葉として オーバーライドの文脈において、アクセス修飾子の指定は権限の割り当てです この権限を弱める場合に該当の表現が用いられます
k.fujisawa

2024/03/06 08:40

大変参考になります
k.fujisawa

2024/03/06 09:37

>public->protectedの場合はオーバーライドが派生クラス自体とそれを継承する子、あるいはパッケージ内のクラスに限定されるでしょう 僕はこのオーバーライドが限定されることは想定しておりませんでした。 アクセス制限のみ緩和・強化されることを想定しておりました。 オーバーライドが限定されない場合に限っては置換原則は保たれていると言えますでしょうか?
TokoToko123

2024/03/06 12:35

派生クラスと基底クラスのメソッドが常に外部から可視な状態ならば置換原則は保たれるでしょう 他回答のC++の例に言及しますが、virtualのないメソッドは基底クラスの実装が優先であり、Javaもフィールドにおいては同一です ですがメソッドに限ってよしなに計らうので、代わりに基準が必要です アクセス修飾子が異なる場合のオーバーライドの動作は本来的に保証外だとも言えるので、なるべく統一することが推奨されます
k.fujisawa

2024/03/07 13:42 編集

<protected->publicの場合> アップキャスト時、アクセス権限はprotectedとなる。 コンパイルチェックでは、インスタンスの型を監視しない。 監視しない以上、protected->protectedかprotected->publicかは保証されない。 <public->protectedの場合> インスタンスの型を監視しない点から、基底クラス型で呼び出し可能とする。 その場合でも、実行時はアクセス制限が守られなければならない。 メソッドディスパッチテーブルにアクセス制限を考慮した仕様が追加される。 つまり、アクセス元の場所によって実装の適用元が変化する。 置換原則「サブタイプはスーパータイプの全ての振る舞いを持つこと」 実装の適用元が変化しても、動作の上ではスーパータイプの振る舞いを 持っていると言えませんか?
TokoToko123

2024/03/12 13:42 編集

言い難いというよりも実用性に薄いでしょう 前述の通り、呼び出されるメソッドが抽象型の場合には実装を持ちません 可視性の低下を許容する場合、煽りを最も受け得るのはインターフェイスです インターフェイスはメソッドの外部公開を強制することで派生型への安全なアクセスをサポートします しかし派生型でアクセス権限が弱化される場合、仮に実装を持たないメソッドが呼ばれる可能性を差し置くとして、そのメソッドがインターフェイスの意図通りに規格化された宣言である保障は担保されません これはジェネリクスとの相性を悪化させます アクセスレベルを統一することで保たれた安全な型判定を破壊するためです
k.fujisawa

2024/03/13 14:44

>インターフェイスはメソッドの外部公開を強制することで派生型への安全なアクセスをサポートします 勉強になります。 置換原則「サブタイプはスーパータイプの全ての振る舞いを持つこと」 これはアップキャストを介すことなく、単純な置き換えが可能である状態を指すようですね そういえば、staticメソッドの場合(隠蔽)も、派生クラスでアクセス権限を弱化できないですね ポリモーフィズムは関係なしに、 置換原則に則って”継承されたメソッドの可視性を下げること"はできないと解釈します。
guest

0

「ポリモーフィズムが破綻するしない」とか「リスコフの置換原則を満たす満たさない」とかとは別の話として、事実上アクセス範囲が狭くならない ならそれができることになんの意味がありますか?

これを許したら
「プログラマがアクセス範囲が狭くしたつもりになっているが実際はアクセス可能」
という事態を引き起こす、バグの温床になるだけの価値がない仕様になりませんか?

(質問で返してすみませんが、なんの意味があるの? というのが率直な感想です。有効な反論がないなら「アクセス修飾子を強める方向にオーバーライドできること」に価値があるとは思えません。「害しか生まない仕様になるから禁止」ではなかろうかと感じました)

投稿2024/03/06 01:18

編集2024/03/06 01:42
quickquip

総合スコア11042

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

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

k.fujisawa

2024/03/06 03:58

いやいや、おっしゃる通りかなと思います。 ただちょっと、設計思想とか気になっちゃって、 聞いてみたかったです。
dameo

2024/03/06 04:41

狭くなった型で呼んだときにアクセスできないという意味があります。 例えば派生クラスであるメソッドをオーバーライドしたけどprivateにして、何か新しいメソッドを追加したとします。 そのクラスを使いたい人は新しいメソッドに用があるのですが、オーバーライドしたメソッドは必ず基底クラスの型で呼んで欲しいという意図が伝わり、更なる派生クラスではオーバーライド自体ができなくなるというメリット?も仕様によっては生まれます。
k.fujisawa

2024/03/06 05:14

その発想は思いつかなかったです。 よくそんなこと思いつきますね。
quickquip

2024/03/06 07:00

> 狭くなった型で呼んだときにアクセスできないという意味があります。 でも「うっかり破ってしまう」ことが簡単に(ここ大事)できちゃいますよね?
k.fujisawa

2024/03/06 07:35

>> 狭くなった型で呼んだときにアクセスできないという意味があります。 >でも「うっかり破ってしまう」ことが簡単に(ここ大事)できちゃいますよね? ・派生クラス外から派生クラス型でアクセスできない。   privateであるため、アクセス権限がない。 ・更なる派生クラスでは派生クラスのものをオーバーライドできない。   privateであるため、継承されない。 ・派生クラス外からアクセスできるのは基底クラス型のみ。 うっかり破ってしまう?破ることはできないのではないでしょうか?
quickquip

2024/03/06 07:50 編集

質問に仮のソースが書いてあって、ここでは「式を基底クラス型にキャストして**アクセスできる**」が前提で、その上で「それ何か問題があるか?」という質問がされているのだと思いましたが?
k.fujisawa

2024/03/06 07:51

・アップキャストが強制的に要求される。 ・ダウンキャストを介する実行が認められない。 ポリモーフィズムの成立なくしてはアクセスできない。  このようなメソッドが作成されることになるかと思います。
quickquip

2024/03/06 07:58 編集

> ・派生クラス外から派生クラス型でアクセスできない。 >  privateであるため、アクセス権限がない。 ここ、今の言語仕様にない動作なので、前提のすりあわせが必要そうですね class Parent { public void some() { ... } } class MethodIsRestricted extends Parent { private void some() { ... } // ここJavaの言語仕様にない操作 public void someOther() { ... some(); ... } } とあった時 Parent p = new MethodIsRestricted(); p.some(); // 動く? 動かない? 質問の前提では「これは動く」で、「アップキャスト程度の **簡単なこと** で簡単に意図せず破られてしまう private になんの意味が?」が回答の主旨です。
quickquip

2024/03/06 08:01

失っているもの(privateがprivateでない)に見合う価値があるか? になるのですが、私にはあまり納得感がないです
k.fujisawa

2024/03/06 08:09

>質問に仮のソースが書いてあって、ここでは「式を基底クラス型にキャストして**アクセスできる**」が前>提で、その上で「それ何か問題があるか?」という質問がされているのだと思いましたが? 大変失礼しました。おっしゃる通りです。 ・サブクラスでアクセス制限をきつくしたのに、既定クラス型からはゆるいアクセス制限から  アクセスできてしまう。狭くしたつもりだがそこに抜け道がある その通りだと思います。失礼しました。 >なんの意味があるの? というのが率直な感想です。 ・派生クラス外からは基底クラス型でのみアクセス可能なメソッドを作成できるかと思います。 逆にポリモーフィズムの実現を促すメソッドを作成できるかと思います。
k.fujisawa

2024/03/06 08:13 編集

>失っているもの(privateがprivateでない)に見合う価値があるか? これは個人の見解になりますが、失っているものに見合う価値はないと思います。
dameo

2024/03/06 08:23 編集

例えばC++だと狭くすることが可能です。オーバーライドはできちゃいますが基底クラスのメソッドが呼べません。 #include <iostream> using namespace std; class Base { public: virtual void func() { cout << "Base::func()" << endl; } }; class Derived: public Base { private: int value; void func() { cout << "Derived::func()" << endl; cout << "valus is " << this->value << endl; } public: Derived(int value): Base(), value(value) {} }; class Derived2: public Derived { private: void func() { cout << "Derived2::func()" << endl; // Derived::func(); // error } public: Derived2(int value): Derived(value) {} }; int main() { Derived d(1); Derived2 d2(1); Base b, &rd = d, &rd2 = d2; // d.func(); // error b.func(); rd.func(); rd2.func(); return 0; } // Base::func() // Derived::func() // valus is 1 // Derived2::func()
k.fujisawa

2024/03/06 08:20

なに!
quickquip

2024/03/06 08:25

コンパイラは素通しするけれど使う側が気をつけろよ! を許容するかしないか、というもう一つの視点があって、Java は許容しない側に倒してますね、という話でもありますね 「コンストラクタの冒頭が絶対に他コンストラクタか親クラスのコンストラクタでなければならない」とかも、(他の言語では許すものが多い中で)コンストラクタの前に独自操作とか許さないぞという気概を感じます
k.fujisawa

2024/03/06 08:30

>Parent p = new MethodIsRestricted(); >p.some(); // 動く? 動かない? >質問の前提では「これは動く」 僕が間違えたせいで、話をややこしくしてすみません。 上記の前提は共通の認識だと思います。 以下は、これが動かないと伝えたかったです。 >MethodIsRestricted p = new MethodIsRestricted(); >p.some(); // > ・派生クラス外から派生クラス型でアクセスできない。 >  privateであるため、アクセス権限がない。
k.fujisawa

2024/03/06 08:32

はえー
dameo

2024/03/06 16:09 編集

素通しというか、例えば以下のような使い方をします。 #include <iostream> #include <memory> using namespace std; class Animal { public: virtual void bark() = 0; virtual ~Animal() {} }; class Dog: public Animal { protected: void bark() { // virtual cout << "Bow-wow" << endl; } }; class Cat: public Animal { protected: void bark() { // virtual cout << "Meow" << endl; } }; class AnimalFactory { public: enum TYPE { DOG, CAT }; static shared_ptr<Animal> create(TYPE t) { switch(t) { case DOG: return make_shared<Dog>(); case CAT: return make_shared<Cat>(); default: return nullptr; } } }; int main() { shared_ptr<Animal> animals[] = { AnimalFactory::create(AnimalFactory::DOG), AnimalFactory::create(AnimalFactory::CAT) }; for (auto animal: animals) { animal->bark(); } return 0; } Factoryを経由してインスタンスが作られており、具象クラスは他の方法で生成されたとしても、そのままでは何も出来ないので、あれ?ってなりますよね(他の方法もあるけど)。設計/実装意図を伝える目的でそういうことをする場合があるという話です。意図しない操作をさせない、という目的で色々な仕様があるのと同様の話です。 Javaのコンストラクタに似たような仕様は、C++のコンストラクタ呼び出しやメンバイニシャライザにもあるかと思います。もっとも「~でなければならない」ではなく、「~にも書ける」のがC++ですが。 Javaが生まれた時期はC++より後なので、多重継承の複雑さに対処していたり、実装が複雑にならないよう、当時としては初心者に優しく配慮された言語になっていると思います。
k.fujisawa

2024/03/07 15:21

勉強になります。
guest

0

Javaでは、メソッドの定義にアクセス修飾子を強制しません
オーバーライド可能な対象は、可視性がprivateではないメソッドです

Java

1import java.util.*; 2 3public class Main { 4 public static void main(String[] args) { 5 Base base=new Sub(); 6 base.test(); 7 } 8} 9 10abstract class Base{ 11 abstract void sample(); 12 13 public void test(){ 14 this.sample(); 15 } 16} 17 18class Sub extends Base{ 19 @Override 20 void sample(){ 21 System.out.println("class Sub"); 22 } 23}
class Sub

アクセス修飾子を持たないメソッドは package privateとして管理され、パッケージ内からのみ参照可能です

以上の通り、アクセス修飾子による修飾は任意です
オーバーライドは外部から呼び出されたメソッドの振る舞いを変更するため、インスタンス外からの参照を禁止するprivateな実装はサポートされません

投稿2024/03/06 00:46

nanashi123

総合スコア51

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

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

k.fujisawa

2024/03/06 04:45

"privateはインスタンス外からの参照を禁止する"では、 語弊があるかもしれないと考えます。 class Base { private void sample(){}; void test() { ((Base)new Sub()).sample(); } } class Sub extends Base {}
nanashi123

2024/03/06 05:31

ここではSubクラスはBaseのインスタンスであり、sampleの呼び出し元は常にBaseの中です(リフレクション除く) ここは言葉の捉え方次第なので特に言及はしません privateの件においては「呼び出し元のBaseからSubを見る手段がない」という点が重要です 逆にSubからはBaseが見えません
k.fujisawa

2024/03/07 14:26

"privateはインスタンス外からの参照を禁止する" この"インスタンス外"は"クラス外"を指しますか? class Base { private void sample(){}; void test() { //Compile Error //The method sample() from the type Base is not visible ((Sub)new Base()).sample(); } } class Sub extends Base {}
k.fujisawa

2024/03/08 14:17

派生クラスの構成インスタンスのうち、基底クラスとの差分インスタンスから 基底クラスのインスタンスが持つprivateメソッドにアクセスできない よって、可視性がprivateなメソッドはオーバーライドすることができない 非常に申し訳ありません!。僕がようやく理解できたのかな?と思います。 >「呼び出し元のBaseからSubを見る手段がない」 こちらは派生クラスの構成インスタンスのうち、基底クラスのインスタンスから 基底クラスとの差分インスタンスが持つprivateメソッドにアクセスできない 以上の認識で正しいでしょうか?すみません。ご確認お願いします。
guest

0

((Object)new java.util.ArrayList()).clone();
Object型としてみたときにclone()が見えるかを判断します。
オーバーライドの成否は関係なく、Objectcloneはそのスコープから見えないのでエラーになっています

java

1Object hoge = new java.util.ArrayList(); 2hoge.clone();

と分解すればわかりやすいでしょうか、hogeの中身は確かにArrayListですが、
変数としての型はあくまでObjectなので、メソッドの可視性はObjectとして判断します。


java アクセス修飾子を強める方向でオーバーライドできないのはなぜでしょうか?

いいえ、できます。
「強める」は「より広い公開範囲に」という意味と解釈しましたが、
protectedObject::clone()に対してpublicArrayList.clone()が用意されている時点でオーバーライドはできています。

投稿2024/03/05 22:28

編集2024/03/06 00:01
ozwk

総合スコア13528

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

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

k.fujisawa

2024/03/06 01:38

すみません! 「強める」は「より狭い公開範囲に」 という意味で使っておりました。
k.fujisawa

2024/03/07 15:04 編集

丁寧に説明していただいた通り、 メソッドの可視性はインスタンスでなくクラスの型で判断される。 そのため、より狭い公開範囲のメソッドでオーバーライドしても、 アップキャストすれば使える。ポリモーフィズムなどの設計思想上、問題無いのでは? このように考えました。
k.fujisawa

2024/03/07 14:56 編集

"弱いアクセス権限"が"公開範囲が狭い"を示すことを知りませんでした。 "アクセス修飾子は制限の強度を示す"で頭がいっぱいでした。 語弊をうむあいまいな使い方をしておりました。申し訳ありません。
k.fujisawa

2024/03/07 15:04

丁寧に説明していただいた通り、 メソッドの可視性はインスタンスでなくクラスの型で判断される。 そのため、より狭い公開範囲のメソッドでオーバーライドしても、 アップキャストすれば使える。ポリモーフィズムなどの設計思想上、問題無いのでは? このように考えました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問