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

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

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

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

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Q&A

解決済

2回答

4589閲覧

Java:抽象クラス・インターフェースで抽象メソッドを宣言する際、アクセス修飾子にprotectedを付けてはいけない理由。

karakorum

総合スコア20

Java

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

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

0グッド

1クリップ

投稿2020/10/24 09:35

編集2020/10/24 10:06

抽象クラス・インターフェースには、抽象メソッドを宣言することができます。
抽象メソッドの宣言時、アクセス修飾子にはpublicを付与する必要があります。
正確には、インターフェースに抽象メソッドを宣言する場合、アクセス修飾子をつけなければ暗黙的にアクセス修飾子publicが付与されます。
このとき、アクセス修飾子がprivateやアクセス修飾子なし、が許されていない理由は分かります。なぜなら、抽象クラスやインターフェースは他のクラスやインターフェースに継承・実装されることを前提に宣言されており、その中に宣言されている抽象メソッドが、同一クラスから、もしくは、同一パッケージからしかアクセス修飾子できないとなれば、抽象クラス・インターフェースの目的と乖離してしまうからです。
ですが、抽象メソッドのアクセス修飾子がprotectedの場合、抽象クラス・インターフェースの目的に沿った動きになるのではないでしょうか?
繰り返しになりますが、抽象クラス・インターフェースは他クラス・インターフェースから継承・実装されることを前提としています。となると「このクラスを継承したサブクラス、または同一パッケージ内のクラスから利用可能」という条件を持つアクセス修飾子protectedを抽象メソッドに付与しても良いのではないでしょうか?
「そもそも、そういうものだから」と言われればそれまでですが、何らかの理由でpublicのみを許しているはずなので、そのあたりの理由はご存じの方がいらっしゃったら、ご回答いただきたいです。
宜しくお願い致します。

###追記① 抽象クラスの抽象メソッドのアクセス修飾子をprotectedに。

package sample; import test.SampleAb; public class Sample extends SampleAb{ public static void main(String args[]){ SampleAb Ab=new Sample(); Ab.method(); } public void method() { System.out.println("抽象メソッドをオーバーライド"); } }
package test; public abstract class SampleAb{ protected abstract void method(); }

➡このとき、Sampleクラスのmainメソッドの下記の部分でコンパイルエラーが起こります。

Ab.method(); メソッド method() は型 SampleAb で不可視です

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

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

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

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

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

LouiS0616

2020/10/24 10:12 編集

※ここのコメントは、回答のコメント欄に移動しました。
karakorum

2020/10/24 10:12

申し訳ございません。 記述ミスです。 修正しましたので、ご確認ください。
gentaro

2020/10/24 11:03

「SampleAb型として宣言している変数」であれば、そのクラスの外部からprotected可視性のmethod()を呼ぶとエラーとなるのは当然です。 具象クラスであるSample型として参照しているのであれば、可視性がpublicに変更されているのでエラーにならないでしょうけど。
karakorum

2020/10/24 11:08

ご回答いただきありがとうございます。 勉強不足で申し訳ありません。 なぜ、 ”SampleAb型として宣言している変数」であれば、そのクラスの外部からprotected可視性のmethod()を呼ぶとエラーとなるのは当然” なのでしょうか? アクセス修飾子protectedであれば、継承しているサブクラスからアクセスできると考えているのですが・・・
gentaro

2020/10/24 11:11

「サブクラスからアクセスできる」の意味合いを思いっきり勘違いしてるんじゃないですか。 それは生成したインスタンスに対するものではなく、メソッドを実装する場合の話ですね。
gentaro

2020/10/24 11:17

そもそも質問文の例が「継承先クラスで継承元クラスの方の変数を継承先クラスのインスタンスとして生成する」というややこしい事をやっているから混乱しているだけで、私の回答で示したように、まずはインスタンスを生成・利用する箇所を分けた実装を見てよく考えてください。
karakorum

2020/10/24 11:26

恐らくアクセス修飾子に関して大きな勘違いをしていることは理解でき、改善したいと思っているのですが、 gentaroさんがおっしゃっている内容がすんなり理解することが出来ていない状況です・・・ ・抽象クラスの抽象メソッドをオーバーライドするとき、対象の抽象メソッドのアクセス修飾子がprotected以上であれば、オーバーライドは可能。しかし、そのメソッドを呼び出す時は、また話が別である。 ここまでの認識はあっているでしょうか?仮にあっているとすると、やはり継承元(親クラス)のメソッドをオーバーライドしているにも関わらず、呼び出せない理由が分かっていません・・・
gentaro

2020/10/24 11:30

私の回答は読んで理解できてますか?
karakorum

2020/10/24 11:37

正直、半分くらいしか理解できていません・・・ 具体的には、 ① 「それは生成したインスタンスに対するものではなく、メソッドを実装する場合の話ですね。」 ➡私の今までの認識と違い、かなら困惑しています。アクセス修飾子protectedの条件「継承しているサブクラスからアクセスできる」が当てはまるのか、分からなくなっています。 ② 「そもそも質問文の例が「継承先クラスで継承元クラスの方の変数を継承先クラスのインスタンスとして生成する」というややこしい事をやっているから混乱しているだけで、私の回答で示したように、まずはインスタンスを生成・利用する箇所を分けた実装を見てよく考えてください。」 ➡生成はできる。しかし、利用する場合、アクセス修飾子がprotectedだと、アクセスできない。この理由も腑に落ちていません
gentaro

2020/10/24 11:44

可視性の基本中の基本を思い出してください。 protected void methodA() を定義したクラスC1があります。 このクラスのインスタンスを生成して、methodA()を呼ぶことは可能ですか?
gentaro

2020/10/24 11:48

何なら入門書を引っ張り出したり、検索してちゃんと確認しましょう。 「あるインスタンスのprotectedメンバを呼ぶことは可能か?」という単純な話です。 これは「不可能」という答えしかありません。 非常に単純な話です。
gentaro

2020/10/24 11:52

おそらく「継承先からアクセスできる」という言葉が心理的なバイアスとして働いていて、「『インスタンスの』protected メンバ」という点を見落としています。 「継承先からアクセスできる」というのは、継承先クラスでメソッドを実装するときなど、 void xxx() { methodA(); //protectedメンバ } のように「インスタンスの」ではなく、「クラスで定義されているメンバの参照」という意味です。
gentaro

2020/10/24 11:54

ここまで言って伝わらないようであれば、ちょっとお手上げです。 可視性について入門書を3回ぐらい読み直して気付いてください。
gentaro

2020/10/24 11:58

なお、質問文の命題である「抽象メソッドを宣言する際、アクセス修飾子にprotectedを付けてはいけない理由」というのがない、というのは回答に示したコードで証明できているはずなので、質問そのものが成立していない、という事でこの質問はクローズできるはずです。
karakorum

2020/10/25 02:51 編集

入門書やネット等で復習をしました。 結論、アクセス修飾子protectedの意味を正しく理解できていませんでした。 現在は、gentaroさんの説明が理解できます。 アクセス修飾子protectedの正しい意味としては、次のように理解を改めました。 親クラスで宣言したアクセス修飾子protectedなメソッドをメソッドA、 親クラスを継承した子クラスで宣言されたアクセス修飾子protectedなメソッドをメソッドA'とした場合、 子クラスからアクセスできるのは、メソッドA'であり、メソッドAではない。 このことから、アクセス修飾子protectedの説明としては、 「このクラスを継承したサブクラスから利用可能」というよりは、 「このクラスを継承したサブクラス内で再定義されたメソッド、もしくは、サブクラスで宣言されている継承されたメソッドにアクセス可能」 という形で解釈しました。
guest

回答2

0

ベストアンサー

まず、抽象クラス・インターフェースとまとめて考えるのはちょっと違うんじゃないかと思います。

インターフェイスは元々、それを実装する**クラスの外部との「契約」**を表すものです。

「このインターフェイスを実装しているクラスはこの操作が行える」というのが基本的な発想になっているので、自然とpublicのみに限定されていました。(最近追加されたデフォルト実装除く)

抽象クラスはどちらかというと継承における「差分プログラミング」のためのものです。
公開メソッドとして抽象メソッドを定義する場合はインターフェイスと似た働きはあるものの、**継承関係のあるクラス同士の契約(具象クラスで実装しなさい、という契約)**と考える事ができます。

したがって、可視性としてはprotectedも許容されます。

親クラスのメソッドの実装において、一部の処理を抽象メソッドにしておくことで、子孫のクラスがその処理フローを利用しながら、一部だけ独自の実装を挿入することが可能になります。

なお、抽象クラスのprotectedメソッドについては以下のような実装をすれば確認できます。

java

1import java.util.*; 2public class Main { 3 public static void main(String[] args) throws Exception { 4 SuperClass obj = new SubClass(); 5 obj.test(); 6 } 7} 8

java

1// 親クラス 2public abstract class SuperClass { 3 protected abstract void method(); 4 public void test() { 5 method(); // protected abstractメソッドの呼び出し(中身は子クラスで実装) 6 } 7}

java

1// 子クラス 2public class SubClass extends SuperClass { 3 @Override 4 protected void method() { 5 System.out.println("抽象メソッドをオーバーライド"); 6 } 7}

投稿2020/10/24 10:47

gentaro

総合スコア8947

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

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

karakorum

2020/10/25 03:02

第一に、抽象クラスとインターフェースを一色淡に考えていました。 ご説明の通り、この2つは目的が全く異なります。 第二に、アクセス修飾子protectedの解釈を誤っていました。 アクセス修飾子protectedの説明として 「このクラスを継承したサブクラス、または同一パッケージ内のクラスから利用可能」 とありますが、正確には、 「このクラスを継承したサブクラス内で再定義されたメソッド、もしくは、サブクラスで宣言されている継承されたメソッドにアクセス可能。または同一パッケージ内のクラスから利用可能」となることを理解しました。 この前提があると、抽象クラスでアクセス修飾子protectedなメソッドが許されることも、私が追記①で記述したソースコードでコンパイルエラーが起こる理由も納得しました。 ありがとうございました。
guest

0

まず一つ目。

抽象クラスの場合、メソッドの可視性~~は任意に選択できます。~~をprotectedにできます。
実際に試してみて下さい。


二つ目。これは個人の考察も含みます。

インターフェースの場合は、おっしゃるとおりpublic修飾しかできません。
これはインターフェースが『このクラスはどうどう扱えるよ』という印としての意味を持つ為です。

確かにprotectedが使いたくなるような場面もありますが、言語の思想ということでしょう。

追記:
Java9以降ではprivateメソッドも定義できるようになったようです。
インターフェースがdefault実装を持つなど、役割が多様化してきたからでしょうね。

さらに追記:
ただし、privateメソッドの場合実装が必須です。
抽象メソッドについては相変わらずpublicのみを認めているようですね。

投稿2020/10/24 09:41

編集2020/10/24 09:49
LouiS0616

総合スコア35668

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

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

karakorum

2020/10/24 10:11

ご回答いただきありがとうございます。 一つ目に関しては、追記①にて実施しましたが、コンパイルエラーが起こりました。 ただ、ポリモフィズムの機能を使わずに、Sample型の変数に生成したSample型のインスタンスのアドレスを格納した場合、実行することができました。 二つ目に関しては、「堂々と扱える」ことを分かり易く示すためにpublicしか許していない、ということは理解できました。 ただ、抽象メソッド自体が継承・実装しないと使うことができない(オーバーライドしないと、そもそも処理がない。)ことが、アクセス修飾子protectedの条件である「このクラスを継承したサブクラスからアクセス可能」という説明とぴったり合っており、なぜprotectedを付与できないのか、いまいち腑に落ちていません・・・。
LouiS0616

2020/10/24 10:16 編集

> 二つ目に関しては、「堂々と扱える」ことを すみません、「どうどう」は「堂々」ではなく「どのように」という意味で使っています。 ググっても同じような用例が見つからないので、私の言葉のチョイスに問題があったようです。
karakorum

2020/10/24 10:19

一つ目に関しては、パッケージを親クラスと子クラスで分けた場合、コンパイルエラー「メソッド method() は型 SampleAb で不可視です」が起こりました。 二つ目に関しては、読み間違いをしていました。 「このクラスはどのように扱えるよ」ということを示す目的でpublicのみを許可している、という点については理解しました。
karakorum

2020/10/24 10:26

ありがとうございます。 よろしくお願いします。
LouiS0616

2020/10/24 10:41

どうやら、this経由のアクセスしか認められていないようです。 https://wandbox.org/permlink/bSZ2FkgTVyiX9Lck 正直、私も勘違いしていました。継承先なら好きにアクセスできるわけでは無いのですね。
LouiS0616

2020/10/24 10:52

いや、『this経由』というのは間違いですね。 Sampleクラスのインスタンスなら呼び出せて当然か... 実際 ((SampleAb)this).method() は通らないですし。
LouiS0616

2020/10/24 11:01

仕様 ( https://docs.oracle.com/javase/specs/jls/se15/html/jls-6.html#jls-6.6.2 ) にはこんな記述があります。 > A protected member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object. "only by code that is responsible for the implementation of that object" というのをどう解釈すれば良いのか...
karakorum

2020/10/24 11:12

読んでみましたが、確かにそのあたりの解釈が難しいですね・・・。
swordone

2020/10/25 03:18

in whichの後の"it"が何を指しているのかわからん…
Zuishin

2020/10/25 03:27

protected に関する説明なので、それに沿って読むのがいいのではないでしょうか。 protected メンバあるいはコンストラクタは、それが宣言されているパッケージ以外からもアクセスできます。ただし、そのオブジェクトの実装をしているコードからのみです。
Zuishin

2020/10/25 03:31

つまり私の解釈では、宣言というのはその protected メンバあるいはコンストラクタを最初に宣言した親クラスでの宣言のことで、他のパッケージでそれを継承した場合、他のパッケージであっても継承したクラスからはアクセスできるということです。
LouiS0616

2020/10/25 03:52 編集

ものすごくふわっとした理解ですけれど、『内部的なアクセスはOK』ということなんですかね。 オーバーライドは可能ですし、またオーバーライドしていない場合に this.method() は ((親クラス)this).method() と同じ意味になるわけですが、これも可能ですし。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問