Javaで、保護されたメンバーが同じパッケージのクラスにアクセスできるようになったのはなぜですか?


14

公式文書から...

モディファイヤクラスパッケージサブクラスワールド 
パブリックYYYY 
保護されたYYYN 
修飾子なしYYNN 
プライベートYNNN 

問題は、同じパッケージ内のクラスから保護されたメンバーにアクセスする必要があるユースケースを覚えていないことです。

この実装の背後にある理由は何ですか?

編集:明確にするために、同じパッケージ内のサブクラスとクラスの両方が保護されたフィールドまたはメソッドにアクセスする必要がある特定のユースケースを探しています。

package some.package;
public class A {
 protected void protectedMethod(){
  // do something
 }
}

package another.package;
public class B extends A{
 public void someMethod(){
  // accessible because B is a subclass of A
  protectedMethod();
 }
} 

package some.package;
public class C {
 public void anotherMethod(){
  // accessible because C is in the same package as A
  protectedMehtod();
 }
}

回答:


4

同じパッケージ内のサブクラスとクラスの両方が保護されたフィールドまたはメソッドにアクセスする必要がある特定のユースケースを探しています...

私にとっては、このようなユースケースは具体的というよりも一般的であり、私の好みから生じたものです。

  1. できるだけ厳密なアクセス修飾子で開始し、必要と思われる場合にのみ、後から弱いものに頼ります。
  2. 単体テストは、テスト済みコードと同じパッケージに常駐させます。

上記から、デフォルトのアクセス修飾子を使用してオブジェクトの設計を開始できます(最初から開始しますprivateが、単体テストは複雑になります)。

public class Example {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        void doSomething() {} // default access
    }
    static class Unit2 {
        void doSomething() {} // default access
    }

    static class UnitTest {
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

サイドノートでは、上記のスニペットでは、Unit1Unit2UnitTestされているネストされた範囲内Exampleのプレゼンテーションを簡単にするために、しかし、実際のプロジェクトでは、私はおそらく別のファイルに(これらのクラスを持っているでしょうUnitTestでも、別のディレクトリに)。

次に、必要が生じた場合、デフォルトからアクセス制御を次のように弱めますprotected

public class ExampleEvolved {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        protected void doSomething() {} // made protected
    }
    static class Unit2 {
        protected void doSomething() {} // made protected
    }

    static class UnitTest {
        // ---> no changes needed although UnitTest doesn't subclass
        // ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

ご覧のExampleEvolvedとおり、オブジェクトへのアクセスはサブクラスではありませんが、同じパッケージから保護されたメソッドにアクセスできるため、ユニットテストコードを変更せずに維持できます。

必要な変更が少ない=>より安全な変更。結局、アクセス修飾子のみを変更し、どのメソッドUnit1.doSomething()と操作を変更しなかったためUnit2.doSomething()、単体テストコードが変更なしで実行を継続することは当然のことです。


5

これには2つの部分があると思います。

  1. クラスは必ずしもカプセル化の適切な単位ではないため、デフォルトの「パッケージ」アクセスは幅広いケースで役立ちます。いくつかのオブジェクトが他のオブジェクトのコレクションとして機能するさまざまな複合オブジェクト。ただし、コレクション全体に不変条件があるため、コレクションにアイテムへの高いアクセス権が必要であるため、アイテムはパブリックに変更できません。C ++には友達がいて、Javaにはパッケージへのアクセスがあります。
  2. 現在、「パッケージ」アクセススコープは、基本的に「サブクラス」(保護された)スコープから独立しています。したがって、パッケージのみ、サブクラスのみ、パッケージとサブクラスの追加のアクセス指定子が必要になります。「パッケージ」スコープは、パッケージ内のクラスのセットが通常明確であり、サブクラスがどこにでも現れる可能性があるため、より制限されています。したがって、物事を単純にするために、Javaは保護されたアクセスにパッケージアクセスを含めるだけで、サブクラスではなくパッケージではない追加の指定子はありません。ほとんどの場合、それprotectedを正確に考える必要があります。

protectedサブクラスのみの方が簡単ではないでしょうか?正直なところ、長い間、私は行動だという印象の下にあった
jramoyo

@jramoyo:いいえ、あなたはまだ何らかの形で結合された動作を利用可能にする必要があるので、それは別の指定子を意味するでしょう。
ヤンフデック

6
@jramoyo-C#では、protectedクラスとサブクラスのみであり、internalライブラリ/パッケージ全体です。protected internalJavaのに相当するものもありprotectedます。
ボブソン

@Bobson-おかげで、C#の実装がより良い選択のようです
jramoyo

5

私見、これはJavaでの悪い設計決定でした。

推測するだけですが、アクセスレベルを厳密に進化させることを望んでいたと思います。プライベート-「パッケージ」-保護-パブリック。一部のフィールドはパッケージで使用可能だがサブクラスでは使用できず、一部のフィールドはサブクラスで使用可能だがパッケージでは使用できず、一部は両方である階層は必要ありませんでした。

それでも、私の謙虚な意見では、彼らは別の方向に行ったはずです:保護されているとはクラスとサブクラスにのみ表示され、そのパッケージはクラス、サブクラス、およびパッケージに表示されます。

多くの場合、サブクラスからはアクセスできる必要があるが、パッケージの他の部分からはアクセスできないクラスにデータがあります。その逆が真実だった場合を考えるのは難しいです。データを共有する必要があるが、そのデータをバンドル外から安全に保持する必要のある相互に関係するクラスのバンドルがいくつかあるというまれな機会がありました。だから、パッケージなどに入れます。しかし、パッケージでデータを共有したいというケースは一度もありませんでしたが、サブクラスからそれを保持したいです。さて、私は状況が近づいていることを想像することができます。このパッケージが、クラスによって拡張される可能性のあるライブラリの一部である場合、私はクラスのデータをプライベートにするのとほぼ同じ理由で、何も知らないでしょう。しかし、クラスとその子だけがデータを利用できるようにしたいのが一般的です。

私と子供の間で私が秘密にしていることはたくさんあり、隣人とは共有しません。私と隣人との間でプライベートなことはほとんどなく、子供と共有しません。:-)


0

すぐに思い浮かぶ良い例は、パッケージで頻繁に使用されるユーティリティクラスです。ただし、パブリックアクセスは必要ありません(舞台裏画像読み込み、ディスクからの作成、ハンドル作成/破棄クラスなど)。 。)の代わりに、すべて[Javaの同等作るのfriend access modifier or idiomではC++他のすべてのクラスのすべての]は自動的に利用可能です。


1
ユーティリティクラスは、メンバーではなく全体として内部的なクラスの例です。
Jan Hudec

0

保護/パッケージアクセス修飾子の使用例は、C ++のフレンドアクセス修飾子の使用例に似ています。

1つのユースケースは、Mementoパターンを実装する場合です。

mementoオブジェクトは、元に戻す操作のチェックポイントとして機能するために、オブジェクトの内部状態にアクセスして保持する必要があります。

Javaには「フレンド」アクセス修飾子がないため、同じパッケージでクラスを宣言することは、Mementoパターンを実現するための可能な方法の1つです。


1
いいえ、mementoオブジェクトにはオブジェクトへのアクセスは必要ないので、アクセスすべきではありません。自身を記憶にシリアル化し、再びシリアル化を解除するのはオブジェクトです。そして、メメント自体は単なる物の言えない財産袋です。どちらも、他方へのパブリックアクセス以上のものであってはなりません。
ジャン・ヒューデック

@JanHudec私は文字通り「メメントパターンを達成するための可能な方法の1つである」と述べました。
Tulainsコルドバ

Mementoパターンを達成するためのさらに別の可能な方法は、すべてを公開することです。つまり、私はあなたの主張を理解できません。
トーマスエディング

-1

対称?

このようなアクセスが必要になることはまれです。そのため、デフォルトのアクセスが使用されることはほとんどありません。しかし、フレームワークは生成されたコードにそれを必要とすることがあります。ラッパークラスは、クラスと直接対話する同じパッケージに配置され、パフォーマンス上の理由でアクセサーではなくメンバーに行きます。


1
ありがとう、例はありますか?そのような音ではなく、「保護」よりも、「デフォルト」アクセサによって達成することができる
jramoyo

-1

Javaのカプセル化階層は明確に定義されています。

クラス->パッケージ->継承

「保護」は、Java設計者が決定したpackage-defaultよりもプライバシーの弱い形式です。package-defaultアイテムへのアクセスは、保護されたアイテムへのアクセスが許可されているエンティティのサブセットに制限されています。

数学的および実装の観点からは、ネストするものにアクセスできるエンティティのセットを持つことは非常に理にかなっています。(クラスは他のパッケージから継承することが許可されているため、保護アクセスセット内にパッケージアクセスセットをネストすることはできませんでした)。

概念的な観点からは、java.utilにあるものをサブクラス化するcom.example.foo.barにあるものよりもパッケージ内の別のクラスと「友好的」であることが理にかなっています。前者の場合、クラスは同じ著者、または少なくとも同じ組織のコーダーによって書かれている可能性があります。


1
「もっと弱いフォーム...」とはどういう意味ですか?
gnat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.