2009-04-03 8 views
7

Sul programma sto scrivendo Ho una classe RestrictedUser e class User che è derivata da RestrictedUser. Sto cercando di nascondere i metodi specifici dell'utente mediante il cast di RestrictedUser, ma quando eseguo il casting i metodi User sono ancora disponibili. Inoltre, quando eseguo il debugger, il tipo di variabile viene visualizzato come utente.Il casting in Java nasconde i metodi e i campi della sottoclasse?

RestrictedUser restricted = regularUser; 

Il casting in Java nasconde i metodi ei campi della sottoclasse o sto facendo qualcosa di sbagliato? C'è una soluzione?

Grazie

+1

Penso che tu possa essere chiaro che cos'è la base e il super qui. Le risposte sembrano confuse. Sarebbe ragionevole avere l'utente limitato la classe base dal momento che può fare di meno. Intuitivamente però, ti troverai nei guai in questo modo dato che in inglese "RestrictedUser" suona più Specializzato che solo Utente. – Hugo

+0

Cosa stai cercando di ottenere comunque? Perché vuoi "nascondere" i metodi della sottoclasse? –

risposta

16

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 la vostra "ristretta" l'utente non è più così limitato.

L'oggetto sta rivelando nel debugger come oggetto User perché è come User oggetto, anche se è memorizzato il riferimento all'oggetto in una variabile RestrictedUser. Fondamentalmente, mettere un'istanza di una classe figlio in una variabile con il tipo di una classe genitore "nasconderà" effettivamente i metodi ei campi della sottoclasse, ma non in modo sicuro.

3

io non sono sicuro di quello che si sta chiedendo come la terminologia che si sta utilizzando è un po 'poco chiaro, ma qui va. Se hai una superclasse e una sottoclasse puoi nascondere i metodi nella superclasse dalla sottoclasse rendendoli privati. Se hai bisogno di metodi pubblici sulla superclasse per essere invisibile sei fuori di fortuna. Se lanci la sottoclasse alla superclasse, i metodi pubblici nella sottoclasse non saranno più visibili.

Un suggerimento che potrebbe essere d'aiuto nell'utilizzare la composizione piuttosto che l'ereditarietà, estrarre i metodi sensibili in una classe separata e quindi inserire un'istanza appropriata di tale classe nell'oggetto, se necessario. È possibile delegare i metodi dalla classe alla classe iniettata, se necessario.

3

Stai confondendo il tipo statico e il tipo dinamico.

Il tipo statico è il tipo di riferimento. Quando si esegue il cast da Derived to Base, si sta dicendo al compilatore che, per quanto ne sa, la cosa a cui punta è una Base. Cioè, stai promettendo che l'oggetto puntato è nullo, o Base, o qualcosa derivato da Base. Vale a dire, ha l'interfaccia pubblica di Base.

Non sarà quindi possibile chiamare i metodi dichiarati in Derivato (e non in Base), ma quando si chiamano i metodi di Base sovrascritti da Derivati, si otterrà la versione sostituita di Derived.

2

Peter's answer e John's answer sono corretti: il solo casting del tipo statico non farà nulla se il tipo reale dell'oggetto (a cui il codice client può eseguire il cast, chiama i metodi di reflection su, ecc.) È ancora disponibile. Devi mascherare il vero tipo in qualche modo (come dice la risposta di Peter).

In questo caso esiste uno schema: dai uno sguardo ai metodi unconfigurable* nella classe Executors, se si ha accesso al codice sorgente JDK.

1

Inoltre, non essere tentato di pensare che sia possibile nascondere i metodi in una classe anonima. Ad esempio, questo non fornirà la privacy che ci si aspetta:

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

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

Potrebbe non essere in grado di nominare il tipo reale di run (al fine di lanciare ad esso direttamente), ma è ancora possibile ottenere la sua classe con getClass(), ed è possibile chiamare secretSquirrel utilizzando la riflessione. (Anche se secretSquirrel è privata, se non avete SecurityManager, o se è stato impostato in modo da consentire l'accessibilità, la riflessione può richiamare i metodi privati ​​anche.)

+0

Supponendo che tu abbia un codice non affidabile (ed è in un altro pacchetto!), Non dovresti essere in grado di invocare il metodo secretSquirrel, perché la classe (anonimo interno) è privata del pacchetto per quanto riguarda il runtime. –

+0

È vero, è un buon punto (ho appena scritto un test per testare anche questo), se il pacchetto è diverso, è necessaria anche l'accessibilità. Grazie! –

+0

Per gli ornitologi e gli archivi: "l'accessibilità è richiesta" significa che è sicuro come Java può farcela. Con l'accessibilità, puoi interrompere l'incapsulamento in qualsiasi modo desideri, ad esempio accedere a campi privati, ecc. Ciò implica che il modello delegato (o qualsiasi altra cosa) non sia difensivo contro di esso. –

3

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

È possibile anche pensare di utilizzare java.lang.reflect.Proxy
[]]

+1

Perché il downvote (dopo 8 mesi)? –

2

Java

In Java, non puoi mai "nascondere" qualcosa da un oggetto. Il compilatore può dimenticare ciò che sai in dettaglio della particolare istanza che hai. (come che sottoclasse è) Il debugger conosce molto più del compilatore, quindi ti dirà di che tipo è 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.

Limitazione aggettivi

Come ho detto in un commento alla domanda, si dovrebbe essere chiari su ciò che è la classe di base qui. Intuitively RestrictedUser dovrebbe essere una sottoclasse di User poiché il nome suggerisce un tipo più specializzato. (Nome con un aggettivo attivo aggiuntivo su di esso.) Nel tuo caso questo è speciale poiché questo è un aggettivo limitante che ti permetterebbe di mettere l'Utente come sottoclasse di RestrictedUser totalmente bene. Vorrei raccomandare di rinominare i due in qualcosa di simile: BasicUser/UserWithId e NonRestrictedUser per evitare l'aggettivo limitante che è la parte confusa qui.

+0

Suggerirei anche questo cambiamento - la classe base dovrebbe avere le funzioni di base di cui TUTTI gli utenti hanno bisogno, e le sottoclassi dovrebbero estendersi e aggiungerle - OOP non sembra essere stato progettato in modo tale che le sottoclassi di supporto non siano in grado di fare cosa possono fare le loro classi genitore. – Knobloch

+0

Sì, potrebbe anche avere la tendenza a scavalcare l'implementazione del metodo "restricted" con un securityException – Hugo

+0

Grazie Hugo per il suggerimento di denominazione :) – tatsuhirosatou

0

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

Se si esegue l'upcast di un UnrestrictedUser a un RestrictedUser, il proprio riferimento sarà in grado di accedere solo ai metodi dichiarati in RestrictedUser. Tuttavia, se lo ritrasferisci a un UnrestrictedUser, avrai accesso a tutti i metodi. Poiché gli oggetti Java sanno che tipo sono realmente, tutto ciò che in realtà è un UnrestrictedUser può essere sempre ricondotto a esso, indipendentemente dal tipo di riferimento che si sta utilizzando (anche uno Object). C'è una parte inevitabile e parte della lingua. Tuttavia, potresti nasconderlo dietro una sorta di proxy, ma nel tuo caso questo probabilmente sconfigge lo scopo.

Inoltre, in Java, tutti i metodi non statici sono virtuali per impostazione predefinita.Ciò significa che se UnrestrictedUser esegue l'override di un metodo dichiarato in RestrictedUser, verrà utilizzata la versione UnrestrictedUser, anche se l'accesso avviene tramite un riferimento RestrictedUser. Se è necessario richiamare la versione della classe genitore, è possibile effettuare una chiamata non virtuale chiamando lo RestrictedUser.variableName.someMethod(). Tuttavia, probabilmente non dovresti usarlo molto spesso (il minimo dei motivi è che rompe l'incapsulamento).

0

Penso anche che questa domanda faccia sorgere un'altra domanda. Come pensi di chiamare questo metodo? Sembra avere una gerarchia di classi in cui la sottoclasse introduce nuove firme di metodo che richiedono l'esecuzione di controlli nei chiamanti.

È possibile aggiornare la domanda per includere uno scenario in cui si chiameranno questi metodi in questione? Forse saremo in grado di aiutare di più.

0

La risposta tecnica è stata data da John Calsbeek. Voglio pensare al problema del design. Quello che stai cercando di fare è impedire ad alcuni segmenti del codice, o ad alcuni sviluppatori, di conoscere qualcosa sulla classe User. Vuoi che trattino tutti gli utenti come se fossero utenti con restrizioni.

Il modo in cui lo si fare è con le autorizzazioni. È necessario impedire agli sviluppatori in questione di accedere alla classe User. Probabilmente puoi farlo inserendolo in un pacchetto e limitando l'accesso al pacchetto.

Problemi correlati