Warum ist ein privates Mitglied in einer statischen Methode zugänglich?

25

Das Folgende ist Pseudocode, ich habe es in Java und PHP ausprobiert und beide haben funktioniert:

class Test { 

    private int a = 5;

    public static function do_test(){
        var t = new Test();
        t.a = 1;
        print t.a // 1
    }

}

Test::do_test();

Warum können Sie dies im OOP-Paradigma tun und wozu dient es?

Ben
quelle
6
Warum sollte es nicht so sein? In Java sind private Mitglieder nicht für die Instanz privat, sondern für die Quelldatei privat . Der erste Sinn ist, equalsdass private Felder einer anderen Instanz überprüft werden müssen. (Posting als Kommentar, da dies kurz ist, und nichts über die OOP-Ness dieses Ansatzes)
Ordous
2
Beachten Sie, dass statische Methoden kein a haben this, sodass die einzigen Objekte ihrer eigenen Klasse die sind, die sie selbst erstellen (oder die als Parameter übergeben werden). Wenn Sie dies als eine Verletzung der Kapselung oder einer Sicherheitslücke betrachten, ist es nicht so, als wäre es eine sehr große Lücke und es lohnt sich möglicherweise nicht, sie zu schließen.
Kilian Foth
4
Ich bin nicht sicher, warum dies abgelehnt wurde. Die Frage mag trivial sein, aber das OP hat sich die Mühe gemacht, das Verhalten in zwei Sprachen zu testen, bevor es gefragt hat. Das ist viel mehr Aufwand, als wir normalerweise von Neulingen sehen.
Yannis
1
@YannisRizos Einverstanden, und in der Tat halte ich die Frage nicht für trivial. Dies hat Auswirkungen auf das "Prinzip der geringsten Privilegien". Dies bedeutet, dass Hilfsfunktionen, die keinen Zugriff auf die Interna einer Instanz benötigen, in einer separaten Klasse definiert werden sollten. Wenn Sie diese Konvention befolgen, wissen Sie, dass auf den internen Status zugegriffen wird, wenn eine statische Methode in derselben Klasse vorhanden ist.
Doval
1
Als ich meine Kollegen fragte, sagten alle, das sei unmöglich. Deshalb habe ich nicht gedacht, dass es trivial ist
Ben

Antworten:

17

In Java sind private Variablen für die gesamte Klasse sichtbar. Auf sie kann über statische Methoden und über andere Instanzen derselben Klasse zugegriffen werden.

Dies ist zum Beispiel bei Factory-Methoden nützlich . Eine Factory-Methode initialisiert normalerweise ein Objekt, das so komplex ist, dass Sie es nicht dem Anwendungscode überlassen möchten. Für die Initialisierung benötigt die Factory-Methode häufig Zugriff auf die Klasseninternale, die Sie nicht verfügbar machen möchten. Der direkte Zugriff auf die privaten Variablen erleichtert Ihnen das Leben erheblich.

Wenn Sie jedoch die Implementierungsdetails einer Klasse auch vor statischen Methoden oder vor anderen Instanzen dieser Klasse verbergen möchten, können Sie dem Datenmuster für private Klassen folgen . Fügen Sie alle privaten Variablen einer Klasse in eine private innere Klasse ein und delegieren Sie alle Getter oder Setter an Getter und Setter dieser inneren Klasse.

Eine andere Möglichkeit besteht darin, eine Schnittstelle für die Klasse zu definieren, die alle öffentlichen Methoden der Klasse deklariert und dann, wo immer möglich, nur auf die Klasse unter dieser Schnittstelle verweist. Ein Verweis auf den Schnittstellentyp kann nicht verwendet werden, um direkt auf etwas zuzugreifen, das nicht in der Schnittstelle deklariert ist, egal wo (außer natürlich mit Reflektion). Wenn Sie eine objektorientierte Programmiersprache verwenden, die keine Schnittstellen hat (wie z. B. C ++), können Sie diese mit einer abstrakten Basisklasse simulieren, die von der eigentlichen Klasse geerbt wird.

interface ITest {
     public int getA();
}

class Test implements ITest { 

    private int a = 5;

    public int getA() { return a; } // implementation of method declared in interface

    public static void main(){
        ITest t = new Test();
        t.a = 1; // syntax error: Interface ITest has no "a"
        System.out.println(t.getA()); // calls Test.getA, visible because ITest declares it
    }

}
Philipp
quelle
Können Sie sich eine Situation vorstellen, in der Datenmuster für private Klassen nützlich sind? Ich persönlich verwende innere Klassen nur in GUI-ähnlichen Setups (Swing usw.) oder statische innere Klassen in Codierungsübungen, da ich nicht möchte, dass sich eine Übung über mehrere Quelldateien erstreckt.
InformedA
1
Wenn Sie die Interna einer Klasse vor anderen Instanzen verbergen, wird einer der Vorteile, die Klassen gegenüber Interfaces haben, zunichte gemacht, ohne dass ein Gegenwert entsteht. Eine einfachere und flexiblere Lösung besteht darin, nur eine Schnittstelle zu verwenden.
Doval
3

Einige Sprachen und Laufzeit-Frameworks (z. B. Java, .NET) gehen davon aus, dass jedem, der den Code für eine bestimmte Klasse kompiliert, vertraut werden kann, dass er keine privaten Member einer Instanz dieser Klasse auf eine Weise verwendet, die sich nachteilig auf deren Richtigkeit auswirkt Operation. Andere Sprachen und Frameworks sind in dieser Hinsicht restriktiver und erlauben keinen Zugriff auf private Mitglieder einer Instanz, außer durch Code, der auf dieser Instanz ausgeführt wird . Beide Ausführungen haben Vor- und Nachteile.

Der größte Vorteil, den Code innerhalb einer Klasse auf private Mitglieder einer Instanz zugreifen zu lassen, besteht darin, dass es Fälle gibt, in denen diese Zugriffsebene angemessen ist. Wenn Sie auf privatediese Weise arbeiten, ist es nicht mehr erforderlich, entweder ein anderes Zugriffsqualifikationsmerkmal für diesen Zweck verfügbar zu haben oder sonst wird der Code gezwungen, Mitglieder breiter darzustellen, als dies ansonsten ideal wäre.

Ein Vorteil des Nichtzulassens eines solchen Zugriffs (wie dies im Microsoft Common Object Model (COM) der Fall war) besteht darin, dass externer Code Klassen als Schnittstellen behandeln kann. Wenn eine Klasse ImmutableMatrixein privates oder geschütztes enthält double[][]dahinter liegendes Feld, und wenn der Code innerhalb der Klasse den Trägers Reihe von anderen Fällen untersucht, dann wird es nicht möglich sein , eine nicht-Array-backed - Klasse zu definieren (zB ZeroMatrix, IdentityMatrix) , die als könnte außerhalb Code verwenden ein Immutable2dMatrix, ohne dass diese Klasse das Hintergrundfeld mit einbeziehen muss. Wenn nichts in Immutable2dMatrixverwendet private Mitglieder einer anderen Instanz als this, dann wäre es möglich, die Klasse umzubenennen ImmutableArrayBackedMatrixund eine neue abstrakte ImmutableMatrixKlasse zu definieren, die ImmutableArrayBackedMatrixals Subtypen auch die oben genannten Klassen ohne Array-Unterstützung haben könnte.

Beachten Sie, dass ein solches Refactoring nicht dadurch verhindert werden würde, dass die Sprache "erlauben" würde ImmutableMatrix, das Backing-Array auf andere Instanzen als zu untersuchen this, es sei denn, die Sprache nutzte diese Fähigkeit und untersuchte tatsächlich externe Instanzen. Der primäre Effekt der Einschränkung einer solchen Verwendung durch eine Sprache besteht darin, dass der Compiler bei jedem Versuch, Code zu schreiben, der für ein solches Refactoring nicht zugänglich wäre, sofort quietscht.

Superkatze
quelle
2

Java ist keine objektorientierte Sprache, sondern eine klassenbasierte Sprache. Die Klasse bestimmt die Operationen und den Verhaltenszugriff und nicht die Instanz.

Seien Sie also nicht allzu überrascht, dass Sie damit Dinge tun können, die nicht streng objektorientiert sind.

Da sich die Methode im selben Klassenbereich wie die Instanz befindet, hat sie vollen Zugriff auf private Mitglieder. Ähnliche Regeln gelten für Instanzen innerer Klassen, die auf Daten aus Instanzen äußerer Klassen zugreifen. Eine Instanz einer inneren Klasse kann auf private Mitglieder der äußeren Klasse zugreifen.

Dies wird von C ++ geerbt, wo es nützlich ist, um Konstruktoren zum Kopieren und Verschieben zu erstellen. Es ist auch nützlich, um zwei Objekte zu vergleichen oder zu kombinieren, deren Wert von Elementen abhängt, auf die nicht auf effiziente Weise öffentlich zugegriffen werden kann (z. B. sollte der Getter für ein Array in Java das Array kopieren, damit der Client-Code es nicht ändern kann und sich ändert der interne Zustand des Objekts, aber das Kopieren von Arrays zum Vergleich der Objektgleichheit ist nicht effizient)

Pete Kirkham
quelle