Ich möchte in der Lage sein, eine Java-Klasse in ein Paket zu schreiben, die auf nicht öffentliche Methoden einer Klasse in einem anderen Paket zugreifen kann, ohne sie zu einer Unterklasse der anderen Klasse machen zu müssen. Ist das möglich?
Hier ist ein kleiner Trick, den ich in JAVA verwende, um den C ++ - Freundmechanismus zu replizieren.
Nehmen wir an, ich habe eine Klasse Romeo
und eine andere Klasse Juliet
. Sie sind aus Hassgründen in verschiedenen Paketen (Familien).
Romeo
will cuddle
Juliet
und Juliet
will sie nur lassen Romeo
cuddle
.
In C ++ Juliet
würde Romeo
als (Liebhaber) deklarieren, friend
aber es gibt keine solchen Dinge in Java.
Hier sind die Klassen und der Trick:
Frauen zuerst :
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
Die Methode Juliet.cuddle
ist also, public
aber Sie brauchen eine Romeo.Love
, um sie aufzurufen. Es verwendet dies Romeo.Love
als "Signatursicherheit", um sicherzustellen, dass nur Romeo
diese Methode aufgerufen werden kann, und überprüft, ob die Liebe echt ist, so dass die Laufzeit eine auslöst, NullPointerException
wenn dies der Fall ist null
.
Jetzt Jungs:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
Die Klasse Romeo.Love
ist öffentlich, ihr Konstruktor jedoch private
. Daher kann es jeder sehen, aber nur Romeo
konstruieren. Ich verwende eine statische Referenz, sodass die Romeo.Love
nie verwendete nur einmal erstellt wird und keine Auswirkungen auf die Optimierung hat.
Daher Romeo
kann cuddle
Juliet
und nur er kann , denn nur so kann er bauen und Zugang eine Romeo.Love
Instanz, die durch erforderlich ist , Juliet
um cuddle
sie (sonst werden sie dich mit einem Schlag NullPointerException
).
Romeo
s‘Love
fürJulia
ewige durch die sich veränderndelove
Feld seinfinal
;-).Die Designer von Java lehnten die Idee eines Freundes ausdrücklich ab, da es in C ++ funktioniert. Sie legen Ihre "Freunde" in das gleiche Paket. Private, geschützte und verpackte Sicherheit wird als Teil des Sprachdesigns erzwungen.
James Gosling wollte, dass Java ohne Fehler C ++ ist. Ich glaube, er hatte das Gefühl, dass ein Freund ein Fehler war, weil er gegen die OOP-Prinzipien verstößt. Pakete bieten eine vernünftige Möglichkeit, Komponenten zu organisieren, ohne OOP zu puristisch zu sehen.
NR wies darauf hin, dass Sie mit Reflection schummeln könnten, aber selbst das funktioniert nur, wenn Sie den SecurityManager nicht verwenden. Wenn Sie die Java-Standardsicherheit aktivieren, können Sie nur dann mit Reflexion betrügen, wenn Sie eine Sicherheitsrichtlinie schreiben, die dies ausdrücklich zulässt.
quelle
friend
OOP verletzt wurde (insbesondere mehr als der Paketzugriff), dann verstand er es wirklich nicht (durchaus möglich, viele Leute verstehen es falsch).Das "Freund" -Konzept ist in Java beispielsweise nützlich, um eine API von ihrer Implementierung zu trennen. Es ist üblich, dass Implementierungsklassen Zugriff auf API-Klasseninternale benötigen, diese sollten jedoch nicht für API-Clients verfügbar gemacht werden. Dies kann mithilfe des unten beschriebenen Musters "Friend Accessor" erreicht werden:
Die Klasse, die über die API verfügbar gemacht wird:
Die Klasse, die die 'Freund'-Funktionalität bereitstellt:
Beispielzugriff von einer Klasse im Implementierungspaket 'friend':
quelle
Es gibt zwei Lösungen für Ihre Frage, bei denen nicht alle Klassen im selben Paket bleiben.
Das erste ist die Verwendung des in (Practical API Design, Tulach 2008) beschriebenen Friend Accessor / Friend Package- Musters.
Die zweite ist die Verwendung von OSGi. Es gibt einen Artikel hier erklärt , wie OSGi dies leistet.
Verwandte Fragen: 1 , 2 und 3 .
quelle
Soweit ich weiß, ist das nicht möglich.
Vielleicht könnten Sie uns weitere Details zu Ihrem Design geben. Fragen wie diese sind wahrscheinlich das Ergebnis von Designfehlern.
Überlegen Sie einfach
quelle
Die Antwort von eirikma ist einfach und ausgezeichnet. Ich könnte noch etwas hinzufügen: Anstatt eine öffentlich zugängliche Methode zu haben, getFriend (), um einen Freund zu erhalten, der nicht verwendet werden kann, könnten Sie einen Schritt weiter gehen und es nicht zulassen, den Freund ohne Token zu erhalten: getFriend (Service.FriendToken). Dieses FriendToken wäre eine innere öffentliche Klasse mit einem privaten Konstruktor, sodass nur der Dienst eine instanziieren könnte.
quelle
Hier ist ein klares Anwendungsbeispiel mit einer wiederverwendbaren
Friend
Klasse. Der Vorteil dieses Mechanismus ist die einfache Verwendung. Vielleicht gut, um Unit-Test-Klassen mehr Zugriff zu gewähren als dem Rest der Anwendung.Hier ist zunächst ein Beispiel für die Verwendung der
Friend
Klasse.Dann können Sie dies in einem anderen Paket tun:
Die
Friend
Klasse ist wie folgt.Das Problem ist jedoch, dass es wie folgt missbraucht werden kann:
Nun kann es wahr sein, dass die
Other
Klasse keine öffentlichen Konstruktoren hat, was den obigenAbuser
Code unmöglich macht. Wenn Ihre Klasse jedoch über einen öffentlichen Konstruktor verfügt, ist es wahrscheinlich ratsam, die Friend-Klasse als innere Klasse zu duplizieren. Nehmen Sie dieseOther2
Klasse als Beispiel:Und dann wäre die
Owner2
Klasse so:Beachten Sie, dass die
Other2.Friend
Klasse einen privaten Konstruktor hat, was dies zu einer viel sichereren Methode macht.quelle
Die bereitgestellte Lösung war vielleicht nicht die einfachste. Ein anderer Ansatz basiert auf der gleichen Idee wie in C ++: Auf private Mitglieder kann außerhalb des Pakets / privaten Bereichs nicht zugegriffen werden, mit Ausnahme einer bestimmten Klasse, die der Eigentümer zu einem Freund von sich selbst macht.
Die Klasse, die einen Freundzugriff auf ein Mitglied benötigt, sollte eine innere öffentliche abstrakte "Freundklasse" erstellen, auf die die Klasse, die die verborgenen Eigenschaften besitzt, den Zugriff exportieren kann, indem sie eine Unterklasse zurückgibt, die die Methoden zur Implementierung des Zugriffs implementiert. Die "API" -Methode der Friend-Klasse kann privat sein, sodass außerhalb der Klasse, die einen Friend-Zugriff benötigt, nicht auf sie zugegriffen werden kann. Die einzige Anweisung ist ein Aufruf eines abstrakten geschützten Elements, das die exportierende Klasse implementiert.
Hier ist der Code:
Zuerst der Test, der bestätigt, dass dies tatsächlich funktioniert:
Dann der Dienst, der Freund Zugriff auf ein privates Paketmitglied von Entity benötigt:
Schließlich: die Entity-Klasse, die einen benutzerfreundlichen Zugriff auf ein privates Paketmitglied nur für die Klasse application.service.Service bietet.
Okay, ich muss zugeben, dass es etwas länger ist als "friend service :: Service;" Möglicherweise kann es jedoch verkürzt werden, während die Überprüfung der Kompilierungszeit mithilfe von Anmerkungen beibehalten wird.
quelle
In Java ist es möglich, eine "paketbezogene Freundlichkeit" zu haben. Dies kann für Unit-Tests nützlich sein. Wenn Sie nicht privat / öffentlich / geschützt vor einer Methode angeben, ist dies "Freund im Paket". Eine Klasse im selben Paket kann darauf zugreifen, ist jedoch außerhalb der Klasse privat.
Diese Regel ist nicht immer bekannt und eine gute Annäherung an ein C ++ - Schlüsselwort "friend". Ich finde es ein guter Ersatz.
quelle
Ich denke, dass Freundklassen in C ++ wie ein Konzept der inneren Klasse in Java sind. Mit inneren Klassen können Sie tatsächlich eine einschließende und eine eingeschlossene Klasse definieren. Die geschlossene Klasse hat vollen Zugriff auf die öffentlichen und privaten Mitglieder der einschließenden Klasse. Siehe den folgenden Link: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
quelle
Ich denke, der Ansatz, das Friend-Accessor-Muster zu verwenden, ist viel zu kompliziert. Ich musste mich dem gleichen Problem stellen und löste es mit dem guten, alten Kopierkonstruktor, der aus C ++ bekannt ist, in Java:
In Ihrer Anwendung können Sie den folgenden Code schreiben:
Der Vorteil dieser Methode ist, dass nur Ihre Anwendung Zugriff auf die geschützten Daten hat. Es ist nicht gerade eine Ersetzung des Schlüsselworts friend. Aber ich denke, es ist ziemlich gut geeignet, wenn Sie benutzerdefinierte Bibliotheken schreiben und auf geschützte Daten zugreifen müssen.
Wann immer Sie sich mit Instanzen von ProtectedContainer befassen müssen, können Sie Ihren ProtectedAccessor darum wickeln und erhalten Zugriff.
Es funktioniert auch mit geschützten Methoden. Sie definieren sie geschützt in Ihrer API. Später in Ihrer Anwendung schreiben Sie eine private Wrapper-Klasse und machen die geschützte Methode als öffentlich verfügbar. Das ist es.
quelle
ProtectedContainer
können Unterklasse außerhalb des Pakets sein!Wenn Sie auf geschützte Methoden zugreifen möchten, können Sie eine Unterklasse der Klasse erstellen, die Sie verwenden möchten, die die Methoden verfügbar macht, die Sie als öffentlich (oder aus Sicherheitsgründen intern im Namespace) verwenden möchten, und eine Instanz dieser Klasse in Ihrer Klasse haben (Verwenden Sie es als Proxy).
Was private Methoden betrifft (glaube ich), haben Sie kein Glück.
quelle
Ich bin damit einverstanden, dass das Schlüsselwort friend in den meisten Fällen nicht erforderlich ist.
Und schließlich, wenn es wirklich notwendig ist, gibt es das in den anderen Antworten erwähnte Friend-Accessor-Muster.
quelle
Kein Schlüsselwort oder so verwenden.
Sie könnten mit Reflexion usw. "betrügen", aber ich würde "betrügen" nicht empfehlen.
quelle
Eine Methode, die ich zur Lösung dieses Problems gefunden habe, besteht darin, ein Accessor-Objekt wie folgt zu erstellen:
Der erste Code, der
getAccessor()
"Claims Ownership" des Accessors aufruft. Normalerweise ist dies Code, der das Objekt erstellt.Dies hat auch einen Vorteil gegenüber dem Freundschaftsmechanismus von C ++, da Sie den Zugriff auf Instanzebene und nicht auf Klassenebene beschränken können . Durch Steuern der Zugriffsreferenz steuern Sie den Zugriff auf das Objekt. Sie können auch mehrere Accessoren erstellen und jedem einen unterschiedlichen Zugriff gewähren, wodurch genau gesteuert werden kann, welcher Code auf was zugreifen kann:
Wenn Sie möchten, dass die Dinge etwas besser organisiert sind, können Sie ein Referenzobjekt erstellen, das alles zusammenhält. Auf diese Weise können Sie alle Accessoren mit einem Methodenaufruf beanspruchen und sie zusammen mit ihrer verknüpften Instanz aufbewahren. Sobald Sie die Referenz haben, können Sie die Accessoren an den Code weitergeben, der sie benötigt:
Nach vielem Headbangen (nicht die gute Art) war dies meine endgültige Lösung und ich mag es sehr. Es ist flexibel, einfach zu bedienen und ermöglicht eine sehr gute Kontrolle über den Klassenzugriff. (Der Zugriff nur mit Referenz ist sehr nützlich.) Wenn Sie für die Accessoren / Referenzen geschützt statt privat verwenden, können Unterklassen von Foo sogar erweiterte Referenzen von zurückgeben
getReference
. Es erfordert auch keine Reflexion, so dass es in jeder Umgebung verwendet werden kann.quelle
Ab Java 9 können Module verwendet werden, um dies in vielen Fällen zu einem Problem zu machen.
quelle
Ich bevorzuge Delegation oder Komposition oder Fabrikklasse (abhängig von dem Problem, das zu diesem Problem führt), um zu vermeiden, dass es eine öffentliche Klasse wird.
Wenn es sich um ein Problem mit "Schnittstellen- / Implementierungsklassen in verschiedenen Paketen" handelt, würde ich eine öffentliche Factory-Klasse verwenden, die sich im selben Paket wie das impl-Paket befindet, und die Offenlegung der impl-Klasse verhindern.
Wenn es sich um ein Problem "Ich hasse es, diese Klasse / Methode öffentlich zu machen, nur um diese Funktionalität für eine andere Klasse in einem anderen Paket bereitzustellen" handelt, würde ich eine öffentliche Delegatenklasse im selben Paket verwenden und nur diesen Teil der Funktionalität verfügbar machen benötigt von der "Außenseiter" Klasse.
Einige dieser Entscheidungen hängen von der Klassenladearchitektur des Zielservers (OSGi-Bundle, WAR / EAR usw.), der Bereitstellung und den Konventionen für die Paketbenennung ab. Zum Beispiel ist das oben vorgeschlagene Lösungsmuster "Friend Accessor" für normale Java-Anwendungen clever. Ich frage mich, ob es aufgrund des unterschiedlichen Klassenladestils schwierig wird, es in OSGi zu implementieren.
quelle
Ich weiß nicht, ob es für irgendjemanden von Nutzen ist, aber ich habe es folgendermaßen gehandhabt:
Ich habe eine Schnittstelle (AdminRights) erstellt.
Jede Klasse, die diese Funktionen aufrufen kann, sollte AdminRights implementieren.
Dann habe ich eine Funktion HasAdminRights wie folgt erstellt:
quelle
Ich habe einmal eine reflexionsbasierte Lösung gesehen, die zur Laufzeit eine "Freundprüfung" mit Reflektion durchführte und den Aufrufstapel überprüfte, um festzustellen, ob die Klasse, die die Methode aufruft, dazu berechtigt war. Als Laufzeitprüfung hat es den offensichtlichen Nachteil.
quelle