Domanda

Nel programma che sto scrivendo ho una lezione RestrictedUser e classe User da cui deriva RestrictedUser. Sto cercando di nascondere i metodi specifici dell'utente trasmettendo a RestrictedUser ma quando eseguo il casting i metodi Utente sono ancora disponibili.Inoltre quando eseguo il debugger viene visualizzato il tipo della variabile User.

RestrictedUser restricted = regularUser;

L'up casting in Java nasconde i metodi e i campi della sottoclasse o sto facendo qualcosa di sbagliato?Esiste una soluzione alternativa?

Grazie

È stato utile?

Soluzione

Se si dovesse tentare di eseguire questo codice:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

si otterrebbe un errore di compilazione. Che non sarà sicuro in qualsiasi modo, forma o forma, perché anche se si dovesse passare intorno alla RestrictedUser, per esempio, un altro metodo, questo metodo potrebbe fare questo:

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

e l'utente "limitato" non è più così limitato.

L'oggetto sta rivelando nel debugger come oggetto User perché è come oggetto User, anche se è memorizzato il riferimento all'oggetto in una variabile RestrictedUser. In sostanza, mettendo un'istanza di una classe figlia in una variabile di tipo di una classe genitore sarà davvero "nascondere" i metodi e campi della sottoclasse, ma non in modo sicuro.

Altri suggerimenti

Non sono sicuro di quello che si sta chiedendo come la terminologia che si sta utilizzando è un po 'poco chiaro, ma qui va. Se si dispone di una superclasse e una sottoclasse è possibile nascondere i metodi della superclasse dalla sottoclasse rendendoli privata. Se avete bisogno di metodi pubblici sulle superclasse per essere invisibili si è fuori di fortuna. Se lanci la sottoclasse alla superclasse poi i metodi pubblici della sottoclasse non sono più visibili.

Un suggerimento che potrebbe aiutare voi sarebbe quella di utilizzare la composizione piuttosto che eredità, estrarre i metodi sensibili in una classe separata e quindi inserire un'istanza appropriata di quella classe nell'oggetto, se necessario. È possibile delegare metodi dalla classe alla classe iniettata se è necessario.

Stai confondendo il tipo statico e il tipo dinamico.

Il tipo statico è il tipo del riferimento.Quando esegui l'upcast da Derived a Base, stai dicendo al compilatore che, per quanto ne sai tu, la cosa puntata è una Base.Cioè, stai promettendo che l'oggetto puntato è null, o Base, o qualcosa derivato da Base.Vale a dire, ha l'interfaccia pubblica di Base.

Non sarai quindi in grado di chiamare i metodi dichiarati in Derived (e non in Base), ma quando chiami qualsiasi metodo Base sovrascritto da Derived, otterrai la versione sovrascritta di Derived.

Se è necessario proteggere la sottoclasse, è possibile utilizzare il modello delegato:

public class Protect extends Superclass {  // better implement an interface here
  private final Subclass subclass;

  public Protect(Subclass subclass) {
    this.subclass = subclass;
  }

  public void openMethod1(args) {
    this.subclass.openMethod1(args);
  }

  public int openMethod2(args) {
    return this.subclass.openMethod2(args);
  }
  ...
}

Si può anche pensare di utilizzare java .lang.reflect.Proxy
[]]

risposta di Pietro e risposta di Giovanni sono corrette: basta lanciare il tipo statico non farà nulla se il vero tipo di oggetto (che codice client può lanciare a, metodi di chiamata di riflessione sulla, ecc) è ancora disponibile. Si deve mascherare il tipo reale in qualche modo (come la risposta di Pietro cita).

Non è in realtà un modello per fare questo: hanno uno sguardo ai metodi unconfigurable* nel Executors classe , se si ha accesso al codice sorgente JDK.

Java

In Java, non si può mai "nascondere" qualcosa da un oggetto. Il compilatore può dimenticare ciò che si sa in dettaglio la particolare istanza che avete. (Come quello che sottoclasse che è) Il debugger sa molto di più di quanto il compilatore, in modo che vi dirà che cosa tipo effettivo l'istanza corrente è, che è probabilmente quello che stai vivendo.

OOP

Sembra che si sta scrivendo la logica che ha bisogno di sapere che tipo di oggetto che si sta utilizzando, che non è raccomandato in OOP, ma spesso necessaria per ragioni pratiche.

Gli aggettivi Limitazione

Come ho detto in un commento alla domanda, si dovrebbe essere chiari su ciò che è la classe di base qui. Intuitivamente RestrictedUser dovrebbe essere una sottoclasse di utente dal momento che il nome suggerisce un tipo più specializzato. (Nome avendo un aggettivo attivo aggiuntivo su di esso.) Nel tuo caso questo è speciale poiché questo è un aggettivo che limita il che permetterà di mettere l'utente come una sottoclasse di RestrictedUser tutto bene. Suggerirei rinominare i due a qualcosa come:. BasicUser / UserWithId e NonRestrictedUser per evitare l'aggettivo che limita che è la parte confusa qui

Inoltre, non siate tentati di pensare di poter nascondere metodi nemmeno in una classe anonima.Ad esempio, questo non fornirà la privacy che ti aspetti:

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

Potresti non essere in grado di nominare il vero tipo di run (per poterlo lanciare direttamente), ma puoi comunque ottenere la sua classe getClass(), e puoi chiamare secretSquirrel utilizzando la riflessione.(Anche se secretSquirrel è privato, se ne hai no SecurityManager, o se è stato impostato per consentire l'accessibilità, la riflessione può invocare anche metodi privati.)

Credo che probabilmente fatto una scelta povera di denominazione:. Penserei RestrictedUser sarebbe una sottoclasse di User, non il contrario, quindi userò UnrestrictedUser come sottoclasse

Se upcast un UnrestrictedUser ad un RestrictedUser, il vostro riferimento sarà solo in grado di accedere ai metodi dichiarati nel RestrictedUser. Tuttavia, se si Downcast di nuovo a un UnrestrictedUser, avrai accesso a tutti i metodi. Dal momento che gli oggetti Java sanno che tipo che realmente sono, tutto ciò che in realtà è un UnrestrictedUser può sempre essere lanciato di nuovo esso, non importa che tipo di riferimento che si sta utilizzando (anche un Object). V'è inevitabile e parte del linguaggio. Tuttavia, si potrebbe nascondere dietro una sorta di delega, ma nel tuo caso che, probabilmente, contrasta con l'obiettivo.

Inoltre, in Java, tutti i metodi non statici sono virtuali per impostazione predefinita. Ciò significa che se UnrestrictedUser sovrascrive alcuni metodo dichiarato in RestrictedUser, allora verrà utilizzata la versione UnrestrictedUser, anche se vi si accede attraverso un riferimento RestrictedUser. Se avete bisogno di richiamare la versione della classe genitore, è possibile effettuare una chiamata non virtuale chiamando RestrictedUser.variableName.someMethod(). Tuttavia, probabilmente non dovrebbe utilizzare questo molto spesso (l'ultimo dei motivi è che si rompe l'incapsulamento).

Credo anche che questa domanda implora un'altra domanda. Come pensate di chiamare questo metodo? Sembra avere una gerarchia di classe in cui sottoclassi introduce nuove firme del metodo richiederà instanceof assegni in chiamanti.

Si può aggiornare la tua domanda per includere uno scenario in cui si desidera chiamare questi metodi in questione? Forse saremo in grado di aiutare di più.

La risposta tecnica è stata data da John Calsbeek. Voglio pensare al problema di progettazione. Quello che si sta cercando di fare è prevenire alcuni segmento del codice, o alcuni sviluppatori, dal sapere nulla della classe User. Volete che trattano tutti gli utenti come se fossero RestrictedUsers.

Il modo in cui si farebbe che è con i permessi. È necessario evitare che gli sviluppatori in questione l'accesso alla classe utente. Probabilmente si può farlo ponendolo in un pacchetto e limitando l'accesso al pacchetto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top