Mam problem z ukrywaniem nazwy, który jest niezwykle trudny do rozwiązania. Oto uproszczona wersja, która wyjaśnia problem:
Jest klasa: org.A
package org;
public class A{
public class X{...}
...
protected int net;
}
Potem jest klasa net.foo.X
package net.foo;
public class X{
public static void doSomething();
}
A teraz mamy problematyczną klasę, która dziedziczy A
i chce zadzwonićnet.foo.X.doSomething()
package com.bar;
class B extends A {
public void doSomething(){
net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
}
}
Jak widzisz, nie jest to możliwe. Nie mogę użyć prostej nazwy, X
ponieważ jest ona ukryta przez dziedziczony typ. Nie mogę użyć w pełni kwalifikowanej nazwy net.foo.X
, ponieważ net
jest ona ukryta przez dziedziczone pole.
Tylko klasa B
jest w mojej bazie kodu; klasy net.foo.X
i org.A
są klasami bibliotecznymi, więc nie mogę ich zmieniać!
Moje jedyne rozwiązanie wygląda następująco: mógłbym wywołać inną klasę, która z kolei wywołuje X.doSomething()
; ale ta klasa istniałaby tylko z powodu konfliktu nazw, który wydaje się bardzo niechlujny! Czy istnieje żadne rozwiązanie, w którym można wywołać bezpośrednio X.doSomething()
z B.doSomething()
?
W języku, który umożliwia określenie globalnej przestrzeni nazw, np. global::
W C # lub ::
C ++, mógłbym po prostu poprzedzić net
ten globalny prefiks, ale Java na to nie pozwala.
źródło
public void help(net.foo.X x) { x.doSomething(); }
i zadzwoń zhelp(null);
net.foo.X
ma metodę, nieorg.A.X
!A
? Dziedziczenie może być takie paskudne, jak odkryłeś…I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy
+1 za postawę czystego kodu. Ale wydaje mi się, że jest to sytuacja, w której należy zrobić kompromis. Po prostu zrób to i wrzuć miły długi komentarz o tym, dlaczego musiałeś to zrobić (prawdopodobnie z linkiem do tego pytania).Odpowiedzi:
Możesz rzutować
null
na typ, a następnie wywołać metodę na tym (co będzie działać, ponieważ obiekt docelowy nie jest zaangażowany w wywołanie metod statycznych).Ma to zalety
net.foo.X
),B
nazwie, którą chcesz, aby miała; dlategoimport static
w Twoim przypadku nie zadziała),Wadą jest to, że ten kod jest naprawdę okropny! Dla mnie generuje ostrzeżenie i ogólnie to dobrze. Ale ponieważ rozwiązuje problem, który w przeciwnym razie jest całkowicie niepraktyczny, dodanie pliku
w odpowiednim (minimalnym!) punkcie zamykającym zamknie kompilator.
źródło
doSomething
metoda gdziekolwiek w twojej hierarchii), więc tak, najlepsze rozwiązanie.Prawdopodobnie najprostszym (niekoniecznie najłatwiejszym) sposobem radzenia sobie z tym byłoby użycie klasy delegatów:
i wtedy ...
Jest to nieco rozwlekłe, ale bardzo elastyczne - możesz sprawić, by zachowywał się w dowolny sposób; ponadto działa w podobny sposób zarówno z
static
metodami, jak i obiektami, których instancje są tworzoneWięcej o obiektach delegatów tutaj: http://en.wikipedia.org/wiki/Delegation_pattern
źródło
Możesz użyć importu statycznego:
Uważaj na to
B
iA
nie używaj nazwanych metoddoSomething
źródło
doSomething
jako nazwy metody wB
…doSomething
gdziekolwiek w hierarchii dziedziczenia (ze względu na sposób rozwiązywania celów wywołania metod). Więc Knuth pomoże ci, jeśli chcesz wywołaćtoString
metodę. Rozwiązanie zaproponowane przez Donala to to, które znajduje się w książce Blocha o Java Puzzlers (myślę, że tego nie sprawdziłem), więc możemy uznać to za autorytatywną odpowiedź :-)Właściwym sposobem wykonywania tego zadania byłby import statyczny, ale w absolutnie najgorszym przypadku MOŻESZ skonstruować instancję klasy przy użyciu funkcji Refleksja, jeśli znasz jej w pełni kwalifikowaną nazwę.
Java: newInstance klasy, która nie ma domyślnego konstruktora
Następnie wywołaj metodę w instancji.
Lub po prostu wywołaj samą metodę z refleksją: wywołanie metody statycznej przy użyciu odbicia
Oczywiście są to oczywiście ostateczności.
źródło
static import
funkcja została dodana tylko wJava 1.5
. Nie zazdroszczę ludziom, którzy muszą rozwijać się do 1.4 lub poniżej, kiedyś musiałem i było okropnie!((net.foo.X)null).doSomething()
rozwiązanie, które działa w starej Javie. Ale jeśli typA
zawiera nawet typ wewnętrznynet
, WTEDY jest to jedyna odpowiedź, która pozostaje ważna :).Nie do końca jest to odpowiedź, ale możesz stworzyć instancję X i wywołać na niej metodę statyczną. To byłby sposób (przyznaję nieprzyzwoity) na nazwanie twojej metody.
źródło
null
na typ, a następnie wywołanie metody byłoby lepsze, ponieważ tworzenie obiektu może mieć efekty uboczne, których nie chcę.null
wydaje się być jednym z czystszych sposobów na zrobienie tego. Musi@SuppressWarnings("static-access")
być wolny od ostrzeżeń…null
, ale rzucanienull
zawsze łamało mi serce.Nie ma potrzeby rzucania ani tłumienia dziwnych ostrzeżeń ani tworzenia zbędnych instancji. Tylko sztuczka wykorzystująca fakt, że możesz wywołać statyczne metody klasy nadrzędnej za pośrednictwem podklasy. (Podobne do mojego hackerskiego rozwiązania tutaj .)
Po prostu stwórz taką klasę
(Prywatny konstruktor w tej ostatniej klasie zapewnia, że nikt nie może przypadkowo utworzyć wystąpienia tej klasy).
Następnie możesz zadzwonić
X.doSomething()
za jego pośrednictwem:źródło
Co jeśli spróbujesz uzyskać przestrzeń nazw gobal, biorąc pod uwagę, że wszystkie pliki znajdują się w tym samym folderze. ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )
źródło
getGlobal()
nie jest standardową metodą Java dla pakietów ... (myślę, że pakiety nie mogą mieć żadnych metod w Javie ...)Jest to jeden z powodów, dla których kompozycja jest lepsza niż dziedziczenie.
źródło
Użyłbym wzorca strategii.
Teraz oddzieliłeś również swoją zależność, więc testy jednostkowe będą łatwiejsze do napisania.
źródło
implements SomethingStrategy
wXSomethingStrategy
deklaracji klasy.