Warum wurden geschützte Mitglieder in Java Klassen desselben Pakets zugänglich gemacht?

14

Aus der offiziellen Dokumentation ...

Modifikator Klasse Paket Unterklasse Welt 
öffentlich JJJJ 
YYYN geschützt 
kein Modifikator YYNN 
private YNNN 

Die Sache ist, ich kann mich nicht erinnern, einen Anwendungsfall gehabt zu haben, in dem ich auf geschützte Mitglieder einer Klasse innerhalb desselben Pakets zugreifen musste.

Was waren die Gründe für diese Implementierung?

Bearbeiten: Zur Verdeutlichung suche ich nach einem bestimmten Anwendungsfall, bei dem sowohl eine Unterklasse als auch eine Klasse innerhalb desselben Pakets auf ein geschütztes Feld oder eine geschützte Methode zugreifen müssen.

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

Antworten:

4

Auf der Suche nach einem bestimmten Anwendungsfall, bei dem sowohl eine Unterklasse als auch eine Klasse innerhalb desselben Pakets auf ein geschütztes Feld oder eine geschützte Methode zugreifen müssen ...

Für mich ist ein solcher Anwendungsfall eher allgemein als spezifisch und ergibt sich aus meinen Vorlieben für:

  1. Beginnen Sie mit einem so strengen Zugriffsmodifikator wie möglich, und greifen Sie erst später auf schwächere Modifikatoren zurück, wenn dies als notwendig erachtet wird.
  2. Unit-Tests befinden sich im selben Paket wie der getestete Code.

Von oben kann ich anfangen, mit Standardzugriffsmodifikatoren für meine Objekte zu entwerfen (ich würde damit beginnen, privateaber das würde den Komponententest erschweren):

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

Randnotiz im obigen Snippet Unit1, Unit2und UnitTestsind verschachtelt in der ExampleEinfachheit der Darstellung, sondern in einem realen Projekt, würde ich wahrscheinlich diese Klassen in separaten Dateien (und habe UnitTestauch in einem separaten Verzeichnis ).

Wenn dann eine Notwendigkeit entsteht, würde ich die Zugriffskontrolle von Standard auf schwächen 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();
        }
    }
}

Sie sehen, ich kann Unit-Test-Code behalten ExampleEvolved unverändert lassen, da geschützte Methoden aus demselben Paket zugänglich sind, obwohl der Zugriff auf das Objekt keine Unterklasse ist .

Weniger Änderungen erforderlich => sicherere Modifikation; Immerhin habe ich nur Zugriffsmodifikatoren geändert und nicht geändert, welche Methoden Unit1.doSomething()und Unit2.doSomething()Aktionen ausgeführt wurden. Daher ist es nur natürlich zu erwarten, dass Unit-Test-Code ohne Änderungen weiter ausgeführt wird.

Mücke
quelle
5

Ich würde sagen, das hat zwei Teile:

  1. Der Standardzugriff "package" ist in vielen Fällen hilfreich, da Klassen nicht immer eine gute Kapselungseinheit sind. Verschiedene zusammengesetzte Objekte, bei denen ein Objekt als Auflistung anderer Objekte fungiert, die Elemente jedoch nicht öffentlich geändert werden dürfen, da es in der gesamten Auflistung einige Invarianten gibt, sodass für die Auflistung ein erhöhter Zugriff auf die Elemente erforderlich ist. C ++ hat Freunde, Java hat Paketzugriff.
  2. Jetzt ist der Zugriffsbereich "package" im Wesentlichen unabhängig vom (geschützten) Bereich "subclass". Daher benötigen Sie zusätzliche Zugriffsspezifizierer nur für das Paket, nur für die Unterklassen sowie für das Paket und die Unterklassen. Der Gültigkeitsbereich "package" ist eingeschränkter, da die Anzahl der Klassen in einem Paket normalerweise festgelegt ist, während eine Unterklasse an einer beliebigen Stelle auftreten kann. Um die Dinge einfach zu halten, bezieht Java den Paketzugriff einfach in den geschützten Zugriff ein und verfügt nicht über einen zusätzlichen Bezeichner für Unterklassen, jedoch nicht für Pakete. Allerdings sollte man sich fast immer protectedgenau das vorstellen.
Jan Hudec
quelle
Wäre es nicht einfacher, wenn protectedes sich nur um Unterklassen handelt? Ehrlich gesagt hatte ich lange Zeit den Eindruck, dass dies das Verhalten ist
jramoyo
@jramoyo: Nein, weil Sie das kombinierte Verhalten immer noch irgendwie verfügbar machen müssen, was einen anderen Bezeichner bedeuten würde.
Jan Hudec
6
@jramoyo - In C # protectednur Klasse und Unterklasse und internalbibliotheks- / paketweit. Es hat auch protected internaldas Äquivalent zu Java protected.
Bobson
@ Bobson - danke, die C # -Implementierung scheint eine bessere Wahl zu sein
jramoyo
5

IMHO, das war eine schlechte Designentscheidung in Java.

Ich spekuliere nur, aber ich denke, sie wollten, dass die Zugriffsebenen streng aufeinander abgestimmt sind: privat - "Paket" - geschützt - öffentlich. Sie wollten keine Hierarchie, in der einige Felder für das Paket, jedoch nicht für Unterklassen verfügbar sind, andere für Unterklassen, jedoch nicht für das Paket, und einige für beide.

Meiner bescheidenen Meinung nach hätten sie den umgekehrten Weg gehen müssen: Gesagt, dass protected nur für die Klasse und die Unterklassen sichtbar ist und dass das Paket für die Klasse, die Unterklassen und das Paket sichtbar ist.

Ich habe oft Daten in einer Klasse, auf die Unterklassen zugreifen können müssen, nicht aber der Rest des Pakets. Es fällt mir schwer, mir einen Fall vorzustellen, in dem das Gegenteil der Fall war. Es gab einige seltene Fälle, in denen ich ein Bündel miteinander verbundener Klassen hatte, die Daten gemeinsam nutzen müssen, die diese Daten jedoch vor dem Zugriff außerhalb des Bündels schützen sollten. Also okay, packen Sie sie in ein Paket usw. Aber ich hatte noch nie einen Fall, in dem das Paket Daten freigeben soll, aber ich möchte, dass es nicht in Unterklassen unterteilt wird. Okay, ich kann mir die Situation vorstellen. Ich nehme an, wenn dieses Paket Teil einer Bibliothek ist, die möglicherweise um Klassen erweitert wird, von denen ich nichts weiß, aus den gleichen Gründen, aus denen ich Daten in einer Klasse privat machen würde. Es ist jedoch weitaus üblicher, Daten nur für die Klasse und ihre Kinder verfügbar zu machen.

Es gibt viele Dinge, die ich zwischen mir und meinen Kindern geheim halte und die wir nicht mit den Nachbarn teilen. Es gibt sehr wenig, was ich privat zwischen mir und den Nachbarn halte und nicht mit meinen Kindern teile. :-)

Jay
quelle
0

Ein gutes Beispiel, das einem sofort in den Sinn kommt, sind Utility-Klassen, die häufig im Paket verwendet werden, aber keinen öffentlichen Zugriff benötigen (Klassen zum Laden von Images von der Festplatte hinter den Kulissen, Erstellen / Zerstören von Handles usw.) .) Anstatt alles [Javas Äquivalent friend access modifier or idiomin C++] von jeder anderen Klasse zu machen, ist alles automatisch verfügbar.

Casey
quelle
1
Utility-Klassen sind eher ein Beispiel für Klassen, die als Ganzes intern sind, als für ihre Mitglieder.
Jan Hudec
0

Die Anwendungsfälle für den Zugriffsmodifikator protected / package ähneln denen für die Zugriffsmodifikatoren friend in C ++.

Ein Anwendungsfall ist die Implementierung des Memento-Musters .

Das Memento-Objekt muss auf den internen Zustand eines Objekts zugreifen, um es zu halten, damit es als Prüfpunkt für das Rückgängigmachen von Vorgängen dient.

Das Deklarieren der Klasse im selben Paket ist eine der Möglichkeiten, um das Memento-Muster zu erzielen, da Java keinen Zugriffsmodifikator "friend" hat .

Tulains Córdova
quelle
1
Nein, das Erinnerungsobjekt muss und sollte keinen Zugriff auf das Objekt haben. Es ist das Objekt, das sich selbst zum Erinnerungsstück serialisiert und wieder deserialisiert. Und das Andenken selbst ist nur eine blöde Eigentumstasche. Keiner sollte mehr als öffentlichen Zugang zum anderen haben.
Jan Hudec
@ JanHudec Ich sagte wörtlich "ist -> einer <- der möglichen Wege, um das Memento-Muster zu erreichen" .
Tulains Córdova
Eine weitere Möglichkeit, das Memento-Muster zu erreichen, besteht darin, alles öffentlich zu machen. Das heißt, ich verstehe Ihren Standpunkt nicht.
Thomas Eding
-1

Symmetrie?

Es ist selten, dass ein solcher Zugriff erforderlich ist, weshalb der Standardzugriff so selten verwendet wird. Aber manchmal möchten Frameworks es für generierten Code, bei dem Wrapper-Klassen in demselben Paket abgelegt werden, das direkt mit Ihren Klassen interagiert, und aus Leistungsgründen an Mitglieder anstatt an Accessoren gehen. Denken Sie an GWT.

jwenting
quelle
1
danke hast du ein beispiel klingt so, kann von einem "Standard" Accessor anstatt "geschützt" erreicht werden
jramoyo
-1

Die Verkapselungshierarchie von Java ist gut definiert:

Klasse -> Paket -> Vererbung

"Geschützt" ist nur eine schwächere Form der Vertraulichkeit als die von den Java-Designern festgelegten Paketstandards. Der Zugriff auf Paketstandardelemente ist auf eine Teilmenge der Entitäten beschränkt, die auf geschützte Elemente zugreifen dürfen.

Aus mathematischer und Implementierungssicht ist es sehr sinnvoll, Ihre Entitätenmengen so zu gestalten, dass sie auf verschachtelte Objekte zugreifen können. (Sie konnten Ihren Paketzugriffssatz nicht in Ihrem geschützten Zugriffssatz verschachteln, da Klassen von anderen Paketen erben dürfen.)

Aus konzeptioneller Sicht ist es sinnvoll, in java.util etwas zu haben, das "freundlicher" mit einer anderen Klasse im Paket ist als etwas in com.example.foo.bar, das diese Klasse unterordnet. Im ersteren Fall werden die Klassen wahrscheinlich vom selben Autor oder zumindest von Programmierern derselben Organisation geschrieben.

Rich Smith
quelle
1
Was bedeutet es "nur eine schwächere Form ..." ?
gnat