web-dev-qa-db-ja.com

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

公式から documentation ...

修飾子クラスパッケージサブクラスワールド
公開Y Y Y Y 
保護されたY Y Y N 
修飾子なしY Y N N 
プライベートY N N N 

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

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

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

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();
 }
}
14
jramoyo

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

私にとっては、このようなユースケースは特定のものではなく一般的であり、それは私の好みから次のように派生します。

  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();
        }
    }
}
_

上記のスニペットのメモ、_Unit1_、_Unit2_およびUnitTestnested 内で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()が実行するメソッドを変更しなかったので、単体テストコードが変更なしで実行し続けることは当然のことです。

4
gnat

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

推測するだけですが、私はアクセスレベルを厳格に発展させたいと考えていました。プライベート-「パッケージ」-保護-パブリック。彼らは階層を持っていたくありませんでした。サブクラスではなくパッケージで利用できるフィールド、サブクラスでは利用できるがパッケージでは利用できないフィールド、およびその両方の階層があります。

それでも、私の控えめな意見では、彼らは反対の方向に進んでいるはずでした:protectedはクラスとサブクラスのみに表示され、そのパッケージはクラス、サブクラス、およびパッケージに表示されます。

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

私と子供たちの間には私が秘密にしておくことがたくさんあり、私たちは隣人と共有しません。私と近所の人との間で秘密を守り、子供たちと共有しないことはほとんどありません。 :-)

5
Jay

これには2つの部分があります。

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

すぐに頭に浮かぶ良い例は、パッケージでよく使用されるユーティリティクラスですが、パブリックアクセスは必要ありません(behind-the-scenes-image-loading-from-disk、handle-creation/destructionクラスなど)。 。)すべてを作成する代わりに[Javaの同等のfriend access modifier or idiomC++]他のすべてのクラスのすべてが自動的に使用可能になります。

0
Casey

Protected/packageアクセス修飾子の使用例は、C++のfriendアクセス修飾子の使用例と似ています。

Mementoパターンを実装する場合が1つの使用例です。

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

Javaにはないため、同じパッケージでクラスを宣言すると、oneでMementoパターンを実現できます。 "friend"アクセス修飾子。

0