Chciałbym móc napisać klasę Java w jednym pakiecie, która może uzyskać dostęp do niepublicznych metod klasy w innym pakiecie bez konieczności tworzenia podklasy drugiej klasy. czy to możliwe?
Oto mała sztuczka, której używam w JAVA do replikacji mechanizmu przyjaciela C ++.
Powiedzmy, że mam klasę Romeo
i inną klasę Juliet
. Są w różnych opakowaniach (rodzinnych) z powodów nienawiści.
Romeo
chce cuddle
Juliet
i Juliet
chce tylko Romeo
cuddle
jej pozwolić .
W C ++ Juliet
zadeklarowałbym Romeo
jako (kochanek), friend
ale java nie ma takich rzeczy.
Oto klasy i sztuczka:
Panie po pierwsze:
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?");
}
}
Więc metoda Juliet.cuddle
jest taka, public
ale trzeba Romeo.Love
ją nazwać. Używa tego Romeo.Love
jako „zabezpieczenia podpisu”, aby upewnić się, że Romeo
może wywołać tę metodę i sprawdza, czy miłość jest prawdziwa, aby środowisko wykonawcze wyrzuciło, NullPointerException
jeśli tak jest null
.
Teraz chłopcy:
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);
}
}
Klasa Romeo.Love
jest publiczna, ale jej konstruktorem jest private
. Dlatego każdy może to zobaczyć, ale tylko Romeo
go zbudować. Używam statycznego odniesienia, więc ten, Romeo.Love
który nigdy nie jest używany, jest budowany tylko raz i nie wpływa na optymalizację.
Dlatego Romeo
może cuddle
Juliet
i tylko on może, ponieważ tylko on może zbudować Romeo.Love
instancję i uzyskać do niej dostęp , co jest Juliet
dla cuddle
niej wymagane (w przeciwnym razie da ci klapsa NullPointerException
).
Romeo
„sLove
doJulia
wiecznego poprzez zmianęlove
pola będziefinal
;-).Projektanci Java wyraźnie odrzucili pomysł przyjaciela, ponieważ działa on w C ++. Umieszczasz swoich „przyjaciół” w tym samym pakiecie. Prywatne, chronione i pakowane zabezpieczenia są wymuszane jako część projektu językowego.
James Gosling chciał, aby Java była C ++ bez błędów. Wierzę, że czuł, że przyjaciel był błędem, ponieważ narusza zasady OOP. Pakiety zapewniają rozsądny sposób organizowania komponentów bez zbytniego purystycznego podejścia do OOP.
NR wskazał, że można oszukiwać za pomocą odbicia, ale nawet to działa tylko wtedy, gdy nie używasz SecurityManager. Jeśli włączysz standardowe zabezpieczenia Java, nie będziesz mógł oszukiwać za pomocą refleksji, chyba że napiszesz zasady bezpieczeństwa, które specjalnie na to pozwalają.
źródło
friend
naruszył OOP (w szczególności więcej niż dostęp do pakietu), to tak naprawdę go nie zrozumiał (całkowicie możliwe, wiele osób źle to rozumie).Koncepcja „przyjaciela” jest przydatna na przykład w Javie do oddzielenia API od jego implementacji. Klasy implementacji często potrzebują dostępu do elementów wewnętrznych klasy API, ale nie powinny one być udostępniane klientom API. Można to osiągnąć za pomocą wzoru „Friend Accessor”, jak opisano poniżej:
Klasa wystawiona przez API:
Klasa zapewniająca funkcjonalność „przyjaciela”:
Przykładowy dostęp z klasy w pakiecie implementacyjnym „przyjaciel”:
źródło
Istnieją dwa rozwiązania twojego pytania, które nie wymagają trzymania wszystkich klas w tym samym pakiecie.
Pierwszym z nich jest użycie wzorca Friend Accessor / Friend Package opisanego w (Practical API Design, Tulach 2008).
Drugim jest użycie OSGi. Jest tu artykuł wyjaśniający, jak OSGi to osiąga.
Powiązane pytania: 1 , 2 i 3 .
źródło
O ile mi wiadomo, nie jest to możliwe.
Może mógłbyś podać nam więcej szczegółów na temat swojego projektu. Takie pytania są prawdopodobnie wynikiem wad projektowych.
Zastanów się
źródło
Odpowiedź eirikma jest łatwa i doskonała. Mógłbym dodać jeszcze jedną rzecz: zamiast mieć publicznie dostępną metodę getFriend (), aby uzyskać przyjaciela, którego nie można użyć, możesz pójść o krok dalej i zabronić zdobywania przyjaciela bez tokena: getFriend (Service.FriendToken). Ten FriendToken byłby wewnętrzną klasą publiczną z prywatnym konstruktorem, tak aby tylko usługa mogła go utworzyć.
źródło
Oto wyraźny przykład użycia z
Friend
klasą wielokrotnego użytku . Zaletą tego mechanizmu jest prostota użytkowania. Może to dobre dla zapewnienia klasom testów jednostkowych większego dostępu niż reszta aplikacji.Na początek poniżej znajduje się przykład korzystania z
Friend
klasy.Następnie w innym pakiecie możesz to zrobić:
Friend
Klasa jest następujący.Problem polega jednak na tym, że można go nadużywać w następujący sposób:
Teraz może być prawdą, że
Other
klasa nie ma żadnych publicznych konstruktorów, przez co powyższyAbuser
kod jest niemożliwy. Jednakże, jeśli klasa nie ma konstruktora publicznego to chyba wskazane, aby powielić klasę Friend jako wewnętrzna klasy. Weź tęOther2
klasę jako przykład:A potem
Owner2
klasa wyglądałaby tak:Zauważ, że
Other2.Friend
klasa ma prywatnego konstruktora, dzięki czemu jest to o wiele bezpieczniejszy sposób.źródło
Podane rozwiązanie może nie było najprostsze. Inne podejście opiera się na tym samym pomyśle, co w C ++: członkowie prywatni nie są dostępni poza pakietem / zakresem prywatnym, z wyjątkiem określonej klasy, którą właściciel sam zaprzyjaźnia.
Klasa, która potrzebuje dostępu znajomego do członka, powinna utworzyć wewnętrzną abstrakcyjną „klasę znajomego”, do której klasa będąca właścicielem ukrytych właściwości może eksportować dostęp, zwracając podklasę, która implementuje metody implementujące dostęp. Metoda „API” klasy znajomego może być prywatna, więc nie jest dostępna poza klasą, która wymaga dostępu znajomego. Jego jedynym stwierdzeniem jest wywołanie abstrakcyjnego elementu chronionego, który implementuje klasa eksportująca.
Oto kod:
Najpierw test sprawdzający, czy to faktycznie działa:
Następnie Usługa, która potrzebuje dostępu znajomego do pakietu prywatnego członka Entity:
Wreszcie: klasa Entity, która zapewnia przyjazny dostęp do prywatnego członka pakietu tylko do klasy application.service.Service.
Okej, muszę przyznać, że jest to trochę dłużej niż „obsługa znajomych :: usługa;” ale możliwe jest skrócenie go przy jednoczesnym zachowaniu sprawdzania czasu kompilacji za pomocą adnotacji.
źródło
W Javie można mieć „przyjazność związaną z pakietami”. Może to być przydatne do testowania jednostkowego. Jeśli nie określisz metody private / public / protected przed metodą, będzie to „przyjaciel w pakiecie”. Klasa w tym samym pakiecie będzie mogła uzyskać do niej dostęp, ale będzie prywatna poza klasą.
Ta reguła nie zawsze jest znana i jest dobrym przybliżeniem słowa kluczowego „przyjaciel” w języku C ++. Uważam, że to dobry zamiennik.
źródło
Myślę, że klasy zaprzyjaźnione w C ++ są jak koncepcja klasy wewnętrznej w Javie. Za pomocą klas wewnętrznych możesz zdefiniować klasę zamykającą i klasę zamkniętą. Klasa zamknięta ma pełny dostęp do publicznych i prywatnych członków jej klasy zamkniętej. zobacz następujący link: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
źródło
Myślę, że podejście polegające na użyciu wzorca dostępu do przyjaciela jest zbyt skomplikowane. Musiałem zmierzyć się z tym samym problemem i rozwiązałem go za pomocą dobrego, starego konstruktora kopii znanego z C ++ w Javie:
W swojej aplikacji możesz napisać następujący kod:
Zaletą tej metody jest to, że tylko twoja aplikacja ma dostęp do chronionych danych. Nie jest to dokładnie podstawienie słowa kluczowego znajomego. Ale myślę, że jest to całkiem odpowiednie, gdy piszesz niestandardowe biblioteki i potrzebujesz dostępu do chronionych danych.
Ilekroć masz do czynienia z wystąpieniami ProtectedContainer, możesz owinąć wokół niego ProtectedAccessor i uzyskać dostęp.
Działa również z metodami chronionymi. Zdefiniujesz je chronione w interfejsie API. Później w aplikacji piszesz prywatną klasę opakowania i udostępniasz chronioną metodę jako publiczną. Otóż to.
źródło
ProtectedContainer
można go sklasyfikować poza pakietem!Jeśli chcesz uzyskać dostęp do metod chronionych, możesz utworzyć podklasę klasy, której chcesz użyć, która ujawnia metody, których chcesz używać jako publicznego (lub wewnętrznego w przestrzeni nazw, aby być bezpieczniejszym) i mieć instancję tej klasy w swojej klasie (użyj go jako proxy).
Jeśli chodzi o metody prywatne (myślę) nie masz szczęścia.
źródło
Zgadzam się, że w większości przypadków słowo kluczowe znajomego jest niepotrzebne.
I wreszcie, jeśli to naprawdę konieczne, istnieje wzorzec akcesorium przyjaciela wymieniony w innych odpowiedziach.
źródło
Nie używasz słowa kluczowego lub czegoś podobnego.
Możesz „oszukiwać” za pomocą refleksji itp., Ale nie polecam „oszukiwania”.
źródło
Znalezioną przeze mnie metodą rozwiązania tego problemu jest utworzenie obiektu akcesora, na przykład:
Pierwszy kod wywołujący
getAccessor()
„roszczenia własności” akcesora. Zwykle jest to kod, który tworzy obiekt.Ma to również przewagę nad mechanizmem przyjaciela C ++, ponieważ pozwala ograniczyć dostęp na poziomie instancji , a nie na poziomie klasy . Kontrolując odwołanie do akcesorium, kontrolujesz dostęp do obiektu. Możesz także utworzyć wiele akcesorów i dać inny dostęp do każdego z nich, co pozwala na szczegółową kontrolę nad tym, jaki kod może uzyskać dostęp do:
Wreszcie, jeśli chcesz, aby rzeczy były nieco lepiej zorganizowane, możesz utworzyć obiekt referencyjny, który trzyma wszystko razem. Dzięki temu możesz przejąć wszystkie akcesory za pomocą jednego wywołania metody, a także zachować je razem z ich połączoną instancją. Po uzyskaniu referencji możesz przekazać akcesoriom kod, który jej potrzebuje:
Po wielu uderzeniach w głowę (nie w dobrym tego rodzaju) było to moje ostateczne rozwiązanie i bardzo mi się podoba. Jest elastyczny, prosty w użyciu i umożliwia bardzo dobrą kontrolę dostępu do klasy. ( Dostęp tylko z referencją jest bardzo przydatny.) Jeśli używasz chronionego zamiast prywatnego dla akcesorów / referencji, podklasy Foo mogą nawet zwracać rozszerzone referencje z
getReference
. Nie wymaga również refleksji, więc można go używać w dowolnym środowisku.źródło
Począwszy od wersji Java 9, w wielu przypadkach można używać modułów, aby nie stanowiły problemu.
źródło
Wolę delegację, skład lub klasę fabryczną (w zależności od problemu, który powoduje ten problem), aby uniknąć uczynienia go klasą publiczną.
Jeśli jest to problem z „klasami interfejsów / implementacji w różnych pakietach”, wówczas użyłbym publicznej klasy fabrycznej, która byłaby w tym samym pakiecie co pakiet impl i zapobiegałaby ujawnieniu klasy impl.
Jeśli jest to problem „Nienawidzę upubliczniać tej klasy / metody tylko po to, aby zapewnić tę funkcjonalność dla innej klasy w innym pakiecie”, wówczas użyłbym publicznej klasy delegowanej w tym samym pakiecie i ujawniłbym tylko tę część funkcjonalności potrzebne klasie „outsider”.
Niektóre z tych decyzji wynikają z architektury ładowania klas serwera docelowego (pakiet OSGi, WAR / EAR itp.), Konwencji wdrażania i nazewnictwa pakietów. Na przykład wyżej zaproponowane rozwiązanie, wzorzec „Friend Accessor” jest sprytny w przypadku normalnych aplikacji Java. Zastanawiam się, czy trudno jest zaimplementować go w OSGi ze względu na różnicę w stylu ładowania klas.
źródło
Nie wiem, czy jest to przydatne dla kogokolwiek, ale poradziłem sobie z tym w następujący sposób:
Stworzyłem interfejs (AdminRights).
Każda klasa, która powinna mieć możliwość wywoływania wspomnianych funkcji, powinna implementować prawa administratora.
Następnie utworzyłem funkcję HasAdminRights w następujący sposób:
źródło
Kiedyś widziałem rozwiązanie oparte na odbiciu, które „sprawdzało przyjaciela” w czasie wykonywania przy użyciu odbicia i sprawdzania stosu wywołań, aby sprawdzić, czy klasa wywołująca metodę może to zrobić. Jako kontrola czasu wykonywania ma oczywistą wadę.
źródło