Auf dem Programm, das ich schreibe, habe ich eine Klasse RestrictedUser und class User, die von RestrictedUser abgeleitet ist. Ich versuche, die benutzerspezifischen Methoden zu verbergen, indem ich nach RestrictedUser wende, aber wenn ich das Casting ausführe, sind die User-Methoden immer noch verfügbar. Auch wenn ich den Debugger starte, erscheint der Typ der Variablen als Benutzer.
%Vor%Verdeckt das Casting in Java die Methoden und Felder der Unterklasse oder mache ich etwas falsch? Gibt es eine Problemumgehung?
Danke
Wenn Sie versuchen würden, diesen Code auszuführen:
%Vor% Sie würden einen Kompilierungsfehler bekommen. Das ist in keiner Weise, Form oder Form sicher, denn selbst wenn Sie RestrictedUser
beispielsweise an eine andere Methode weitergeben würden, könnte diese Methode Folgendes tun:
und Ihr "eingeschränkter" Benutzer ist nicht mehr so eingeschränkt.
Das Objekt wird im Debugger als User
-Objekt angezeigt, weil es als User
-Objekt ist, auch wenn die Objektreferenz in einer RestrictedUser
-Variable gespeichert ist. Wenn Sie eine Instanz einer untergeordneten Klasse in eine Variable mit dem Typ einer übergeordneten Klasse einfügen, werden die Methoden und Felder der Unterklasse zwar "versteckt", aber nicht sicher.
Ich bin mir nicht sicher, was genau Sie fragen, da die Terminologie, die Sie verwenden, etwas unklar ist, aber hier geht es. Wenn Sie eine Oberklasse und eine Unterklasse haben, können Sie die Methoden in der Oberklasse aus der Unterklasse ausblenden, indem Sie sie als privat definieren. Wenn Sie öffentliche Methoden für die Superklasse brauchen, um unsichtbar zu sein, haben Sie kein Glück. Wenn Sie die Unterklasse in die Oberklasse umwandeln, sind die öffentlichen Methoden der Unterklasse nicht mehr sichtbar.
Ein Vorschlag, der Ihnen helfen könnte, Komposition anstelle von Vererbung zu verwenden, extrahiert die sensitiven Methoden in eine separate Klasse und fügt dann bei Bedarf eine geeignete Instanz dieser Klasse in das Objekt ein. Sie können bei Bedarf Methoden aus der Klasse an die injizierte Klasse delegieren.
Sie verwechseln statischen Typ und dynamischen Typ.
Statischer Typ ist der Typ der Referenz. Wenn Sie von "Abgeleitet" in "Basis" up-casten, sagen Sie dem Compiler, dass, soweit Sie und es wissen, das Ding auf eine Base zeigt. Das heißt, Sie versprechen, dass das Objekt, auf das gezeigt wird, entweder Null oder eine Basis oder etwas ist, das von Base abgeleitet ist. Das heißt, es hat die öffentliche Schnittstelle von Base.
Sie können dann keine Methoden aufrufen, die in Derived (und nicht in Base) deklariert sind. Wenn Sie jedoch eine Base-Methode aufrufen, die von Derived überschrieben wurde, erhalten Sie die überschriebene Version von Derived.
Wenn Sie die Unterklasse schützen müssen, können Sie das Delegiertenmuster verwenden:
%Vor% Sie können auch daran denken, java.lang.reflect.Proxy zu verwenden
[]]
Peters Antwort und Johns Antwort sind korrekt: Wenn man nur den statischen Typ ausgibt, wird nichts unternommen, wenn der reale Typ des Objekts (zu dem der Client-Code wechseln kann, Reflektionsmethoden aufrufen usw.) immer noch verfügbar ist. Sie müssen den realen Typ irgendwie maskieren (wie Peters Antwort erwähnt).
Dafür gibt es tatsächlich ein Muster: Sehen Sie sich die Methoden unconfigurable*
im Executors
Klasse, wenn Sie Zugriff auf den JDK-Quellcode haben.
Java
In Java können Sie niemals etwas von einem Objekt "ausblenden". Der Compiler kann vergessen, was Sie genau über Ihre bestimmte Instanz wissen. (Wie die Unterklasse es ist) Der Debugger weiß viel mehr als der Compiler, also wird es Ihnen sagen, welcher tatsächliche Typ die aktuelle Instanz ist, was wahrscheinlich ist, was Sie erleben.
OOP
Es klingt, als ob Sie eine Logik schreiben, die wissen muss, welche Art von Objekt Sie verwenden, was in OOP nicht empfohlen wird, aber oft aus praktischen Gründen erforderlich ist.
Beschränkung von Adjektiven
Wie ich in einem Kommentar zu der Frage gesagt habe, sollten Sie sich hier über die Basisklasse klar sein. Intuitiv RestrictedUser sollte eine Unterklasse von User sein, da der Name einen spezielleren Typ vermuten lässt. (Name mit einem zusätzlichen aktiven Adjektiv.) In diesem Fall ist dies etwas Besonderes, da dies ein begrenzendes Adjektiv ist, mit dem Sie den Benutzer als eine Unterklasse von RestrictedUser völlig in Ordnung bringen können. Ich würde empfehlen, die beiden zu etwas wie: BasicUser / UserWithId und NonRestrictedUser umzubenennen, um das begrenzende Adjektiv zu vermeiden, das hier der verwirrende Teil ist.
Seien Sie auch nicht versucht zu denken, dass Sie auch Methoden in einer anonymen Klasse verstecken können. Zum Beispiel bietet dies nicht die Privatsphäre, die Sie erwarten:
%Vor% Sie können möglicherweise den realen Typ von run
nicht benennen (um direkt darauf zu werfen), aber Sie können seine Klasse immer noch mit getClass()
erhalten und Sie können secretSquirrel
mit Reflection aufrufen. (Auch wenn secretSquirrel
privat ist, wenn Sie kein SecurityManager
haben oder wenn es eingerichtet wurde, um die Barrierefreiheit zuzulassen, kann Reflection auch private Methoden aufrufen.)
Ich denke, Sie haben wahrscheinlich eine schlechte Namenswahl getroffen: Ich würde denken, RestrictedUser
wäre eine Unterklasse von User
, nicht umgekehrt, also verwende ich UnrestrictedUser
als Unterklasse.
Wenn Sie eine UnrestrictedUser
auf eine RestrictedUser
erhöhen, kann Ihre Referenz nur auf Methoden zugreifen, die in RestrictedUser
deklariert sind. Wenn Sie es jedoch zurück auf UnrestrictedUser
reduzieren, haben Sie Zugriff auf alle Methoden. Da Java-Objekte wissen, welcher Typ sie wirklich sind, kann alles, was tatsächlich ein UnrestrictedUser
ist, immer wieder zurückgeworfen werden, unabhängig davon, welche Art von Verweis Sie verwenden (sogar ein Object
). Es ist unvermeidlich und Teil der Sprache. Sie könnten es jedoch hinter einer Art Proxy verstecken, aber in Ihrem Fall wird das wahrscheinlich den Zweck verfehlen.
Auch in Java sind alle nicht statischen Methoden standardmäßig virtuell. Das heißt, wenn UnrestrictedUser
eine in RestrictedUser
deklarierte Methode überschreibt, wird die UnrestrictedUser
Version verwendet, auch wenn Sie über eine RestrictedUser
Referenz darauf zugreifen. Wenn Sie die Elternklassenversion aufrufen müssen, können Sie einen nicht virtuellen Anruf tätigen, indem Sie RestrictedUser.variableName.someMethod()
aufrufen. Sie sollten dies jedoch wahrscheinlich nicht sehr oft verwenden (der geringste Grund dafür ist, dass es die Kapselung unterbricht).
Ich denke auch, dass diese Frage eine andere Frage aufwirft. Wie planen Sie, diese Methode zu nennen? Es scheint so zu sein, als ob eine Klassenhierarchie, in der Unterklassen neue Methodensignaturen einführen, eine Instanz von Checks in Anrufern erfordert.
Können Sie Ihre Frage aktualisieren, um ein Szenario einzubeziehen, in dem Sie diese Methoden in Frage stellen würden? Vielleicht können wir mehr helfen.
Die technische Antwort wurde von John Calsbeek gegeben. Ich möchte über das Designproblem nachdenken. Sie versuchen zu verhindern, dass ein Teil des Codes oder einige Entwickler etwas über die Benutzerklasse wissen. Sie möchten, dass alle Benutzer wie eingeschränkte Benutzer behandelt werden.
Wie Sie das tun würden, ist mit Berechtigungen. Sie müssen verhindern, dass die betreffenden Entwickler Zugriff auf die Benutzerklasse haben. Sie können das wahrscheinlich tun, indem Sie es in ein Paket einfügen und den Zugriff auf das Paket beschränken.