2009-04-03 17 views
7

W programie, który piszę, mam klasę RestrictedUser i Class User, która pochodzi od RestrictedUser. Próbuję ukryć metody specyficzne dla użytkownika, przesyłając je do RestrictedUser, ale kiedy wykonuję casting, metody użytkownika są nadal dostępne. Również po uruchomieniu debuggera typ zmiennej pojawia się jako Użytkownik.Czy odlewanie w Javie ukrywa metody i pola podklasy?

RestrictedUser restricted = regularUser; 

Czy odlewanie w Javie ukrywa metody i pola podklasy lub czy robię coś nie tak? Czy jest w pobliżu praca?

Dzięki

+1

Myślę, że musicie wyjaśnić, co jest tutaj podstawą i super. Odpowiedzi wydają się zagmatwane. Byłoby uzasadnione, aby restuserUser był klasą podstawową, ponieważ może zrobić mniej. Intuicyjnie jednak wpadniesz w kłopoty w ten sposób, ponieważ w języku angielskim "RestrictedUser" brzmi bardziej wyspecjalizowany niż tylko użytkownik. – Hugo

+0

Co i tak próbujesz osiągnąć? Dlaczego chcesz "ukryć" metody podklasy? –

Odpowiedz

16

Jeśli było próbować uruchomić ten kod:

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

byś się błąd kompilacji. Że nie będą bezpieczne w jakikolwiek sposób, kształt lub formę, bo nawet jeśli były, aby przejść wokół RestrictedUser do, powiedzmy, kolejny sposób, że metoda może zrobić to:

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

i twoi „zastrzeżone” użytkownik nie jest już tak ograniczony.

Obiekt jest wyświetlany w debugerze jako obiekt User, ponieważ jest to obiekt User, nawet jeśli odwołanie do obiektu jest przechowywane w zmiennej RestrictedUser. Zasadniczo, umieszczenie instancji klasy potomnej w zmiennej o typie klasy nadrzędnej rzeczywiście "ukryje" metody i pola podklasy, ale nie będzie to bezpieczne.

3

Nie jestem pewien, co dokładnie pytasz jak terminologii używanej jest trochę niejasne, ale tu idzie. Jeśli masz superklasę i podklasę, możesz ukryć metody w superklasie podklasy, czyniąc je prywatnymi. Jeśli chcesz, aby publiczne metody nadklasy były niewidoczne, nie masz szczęścia. Jeśli rzucisz podklasę na superklasę, wtedy publiczne metody na podklasie nie będą już widoczne.

Jedną z sugestii, która może pomóc w korzystaniu z kompozycji, a nie dziedziczenia, jest wyodrębnienie wrażliwych metod w oddzielnej klasie, a następnie wstawienie odpowiedniej instancji tej klasy do obiektu w razie potrzeby. Jeśli chcesz, możesz przekazać metody z klasy do wstrzykniętej klasy.

3

Dezorientujesz typ statyczny i dynamiczny.

Typ statyczny to typ odniesienia. Kiedy rzucasz w górę z Derived do Base, mówisz kompilatorowi, że tak daleko jak ty i on wie, rzecz wskazuje na Base. Oznacza to, że obiecujesz, że wskazany obiekt jest albo zerowy, albo podstawa, lub coś wyprowadzonego z bazy. Co oznacza, że ​​ma publiczny interfejs Base.

Nie będziesz wtedy mógł wywoływać metod zadeklarowanych w Pochodnym (a nie w Bazie), ale kiedy wywołasz jakiekolwiek Metody bazowe nadpisane przez Pochodną, ​​otrzymasz nadpisaną wersję Derived.

2

Peter's answer i John's answer są poprawne: po prostu rzucając statyczny typ zrobi nic, jeśli rzeczywisty typ obiektu (którego kod klient może rzucać, sposoby na wywołanie refleksji, etc.) jest nadal dostępna. Musisz jakoś maskować prawdziwy typ (jak wspomina odpowiedź Petera).

Istnieje na to pewien wzór: zapoznaj się z metodami unconfigurable* w klasie Executors, jeśli masz dostęp do kodu źródłowego JDK.

1

Nie należy także kusić, aby można było ukryć metody w anonimowej klasie. Na przykład, to nie zapewni prywatność, czego się spodziewać:

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

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

może nie być w stanie nazwać prawdziwą typ run (aby rzucić na niego bezpośrednio), ale wciąż można dostać swoją klasę getClass() i możesz zadzwonić pod numer secretSquirrel za pomocą odbicia. (Nawet jeśli secretSquirrel jest prywatny, jeśli nie masz SecurityManager, lub jeśli został on ustanowiony w celu umożliwienia dostępu, odbicie może wywoływać metody prywatne też.)

+0

Zakładając, że masz niezaufany kod (i jest on w innej paczce!), Nie powinieneś być w stanie wywołać metody secretSquirrel, ponieważ klasa (anonimowa wewnętrzna) jest pakietem prywatnym w odniesieniu do środowiska wykonawczego. –

+0

To prawda, że ​​to dobry punkt (napisałem test, aby przetestować to również), jeśli pakiet jest inny, wymagana jest również dostępność. Dzięki! –

+0

Dla lurków i archiwów: "dostępność jest wymagana" oznacza, że ​​jest tak bezpieczna, jak może to zrobić Java. Dzięki dostępności możesz przerwać enkapsulację w dowolny sposób, np. Uzyskać dostęp do prywatnych pól itp. Oznacza to, że delegowany wzorzec (lub cokolwiek innego) jest bezbronny wobec niego. –

3

Jeśli trzeba chronić podklasę, można użyć wzoru Delegat:

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

można również pomyśleć o użyciu java.lang.reflect.Proxy
[]]

+1

Dlaczego spadły (po 8 miesiącach)? –

2

Java

W Javie nigdy nie można "ukryć" czegoś przed obiektem. Kompilator może zapomnieć o szczegółach dotyczących konkretnej instancji. (jak to jest podklasa) Debugger wie o wiele więcej niż kompilator, więc powie ci, jaki aktualny typ jest obecną instancją, która prawdopodobnie jest tym, czego doświadczasz.

OOP

To brzmi jak piszesz logiki, który musi wiedzieć, jaki rodzaj obiektu, którego używasz, który nie jest zalecany w OOP, ale często wymagane ze względów praktycznych.

Ograniczanie Przymiotniki

Jak powiedział w komentarzu na pytanie, powinno być jasne, na co jest klasą bazową tutaj. Intuicyjnie RestrictedUser powinien być podklasą User, ponieważ nazwa sugeruje bardziej wyspecjalizowany typ. (Nazwa posiadająca dodatkowy aktywny przymiotnik.) W twoim przypadku jest to wyjątkowe, ponieważ jest to przymiotnik ograniczający, który pozwoliłby ci ustawić użytkownika jako podklasę RestrictedUser całkowicie w porządku. Polecam zmianę nazwy tych dwóch na coś takiego: BasicUser/UserWithId i NonRestrictedUser, aby uniknąć przymiotnika ograniczającego, który jest tutaj mylący.

+0

Ja też doradziłbym tę zmianę - klasa podstawowa powinna mieć podstawowe funkcje wymagane przez WSZYSTKICH użytkowników, a podklasy powinny rozszerzać i dodawać do nich - OOP nie wygląda na zaprojektowaną tak, aby obsługa podklas nie była w stanie tego zrobić co mogą zrobić zajęcia rodziców. – Knobloch

+0

Tak, może to nawet spowodować nadpisanie implementacji metody "zastrzeżonej" z securityException – Hugo

+0

Dzięki Hugo za wskazówkę nazywania :) – tatsuhirosatou

0

myślę, że prawdopodobnie popełnił słabe wybór nazewnictwa: Myślę RestrictedUser będzie podklasą User, a nie na odwrót, więc użyję UnrestrictedUser jako podklasy.

Jeśli upcast UnrestrictedUser do , twoje odniesienie będzie w stanie uzyskać dostęp do metod zadeklarowanych w RestrictedUser. Jeśli jednak spadnie z powrotem do UnrestrictedUser, będziesz miał dostęp do wszystkich metod. Ponieważ obiekty Java wiedzą, jaki naprawdę są, wszystko, co w rzeczywistości jest UnrestrictedUser, może być zawsze odesłane do niego, bez względu na to, jakiego rodzaju odniesienia używasz (nawet Object). Jest nieunikniony i część języka. Możesz go jednak ukryć za jakimś proxy, ale w twoim przypadku prawdopodobnie pokonasz cel.

Ponadto w języku Java wszystkie metody niestatyczne są domyślnie wirtualne.Oznacza to, że jeśli UnrestrictedUser nadpisze niektóre metody zadeklarowane w RestrictedUser, zostanie użyta wersja UnrestrictedUser, nawet jeśli uzyskasz do niej dostęp poprzez referencję RestrictedUser. Jeśli chcesz wywołać wersję klasy nadrzędnej, możesz nawiązać połączenie nie-wirtualne, dzwoniąc pod numer RestrictedUser.variableName.someMethod(). Jednak prawdopodobnie nie powinieneś używać tego bardzo często (a przynajmniej dlatego, że łamie hermetyzację).

0

Też myślę, że to pytanie rodzi kolejne pytanie. Jak zamierzasz nazwać tę metodę? Wygląda na to, że posiadanie hierarchii klasy, w której tworzenie podklasy wprowadza nowe sygnatury metod, będzie wymagało sprawdzania instancji w wywołaniach.

Czy możesz zaktualizować swoje pytanie, aby uwzględnić scenariusz, w którym można by je nazwać? Być może będziemy w stanie pomóc więcej.

0

Techniczną odpowiedź dostarczył John Calsbeek. Chcę pomyśleć o kwestii projektowania. Próbujesz uniemożliwić niektórym segmentom kodu lub niektórym programistom uzyskanie jakichkolwiek informacji o klasie użytkownika. Chcesz, aby traktowali wszystkich Użytkowników tak, jakby byli RestrictedUsers.

Sposób, w jaki można to zrobić, to z uprawnieniami. Musisz uniemożliwić deweloperom, których to dotyczy, dostęp do klasy User. Prawdopodobnie możesz to zrobić, umieszczając go w pakiecie i ograniczając dostęp do pakietu.