Eksperymentuję z tym kodem:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
To drukuje foo(Object o)
trzy razy. Oczekuję, że wybór metody będzie uwzględniał rzeczywisty (a nie deklarowany) typ parametru. Czy coś mi brakuje? Czy istnieje sposób na zmodyfikowanie tego kodu, aby był drukowany foo(12)
, foo("foobar")
i foo(Object o)
?
foo(Object)
. W czasie wykonywania klasa obiektu, dla którego wywoływana jest metoda, określa, która implementacja tej metody jest wywoływana, biorąc pod uwagę, że może to być instancja podklasy zadeklarowanego typu, która przesłania metodę.Jak wspomniano wcześniej, rozpoznawanie przeciążenia jest wykonywane w czasie kompilacji.
Java Puzzlers ma na to dobry przykład:
Zagadka 46: Przypadek zagubionego konstruktora
Ta łamigłówka przedstawia dwóch konstruktorów Mylących. Główna metoda wywołuje konstruktora, ale który? Wynik programu zależy od odpowiedzi. Co program drukuje, czy w ogóle jest to legalne?
Rozwiązanie 46: Przypadek mylącego konstruktora
... Proces rozwiązywania problemów w Javie przebiega w dwóch fazach. Pierwsza faza wybiera wszystkie metody lub konstruktory, które są dostępne i mają zastosowanie. Druga faza wybiera najbardziej szczegółowe metody lub konstruktory wybrane w pierwszej fazie. Jedna metoda lub konstruktor jest mniej specyficzna niż inna, jeśli może akceptować parametry przekazane do drugiej [JLS 15.12.2.5].
W naszym programie oba konstruktory są dostępne i mają zastosowanie. Konstruktor Confusing (Object) akceptuje każdy parametr przekazany do Confusing (double []) , więc Confusing (Object) jest mniej konkretny. (Każda podwójnie tablica jest obiekt , jednak nie każdy obiekt jest podwójna tablica ). Od najbardziej konstruktor więc kłopotliwe (dwukrotnie []) , co wyjaśnia wyjście programu.
To zachowanie ma sens, jeśli przekażesz wartość typu double [] ; jeśli przekażesz null, jest sprzeczne z intuicją . Kluczem do zrozumienia tej zagadki jest to, że test, dla której metoda lub konstruktor jest najbardziej specyficzny, nie wykorzystuje rzeczywistych parametrów : parametrów pojawiających się w wywołaniu. Służą one jedynie do określenia, które przeciążenia mają zastosowanie. Gdy kompilator ustali, które przeciążenia mają zastosowanie i są dostępne, wybiera najbardziej specyficzne przeciążenie, używając tylko parametrów formalnych: parametrów pojawiających się w deklaracji.
Aby wywołać konstruktor Confusing (Object) z parametrem null , napisz nowy Confusing ((Object) null) . Gwarantuje to, że ma zastosowanie tylko Confusing (Object) . Mówiąc bardziej ogólnie, aby zmusić kompilator do wybrania określonego przeciążenia, należy rzutować rzeczywiste parametry na zadeklarowane typy parametrów formalnych.
źródło
Możliwość wysłania wywołania metody opartej na typach argumentów nazywana jest wysyłaniem wielokrotnym . W Javie odbywa się to za pomocą wzorca Visitor .
Jednakże, ponieważ masz do czynienia z
Integer
s iString
s, nie możesz łatwo włączyć tego wzorca (po prostu nie możesz modyfikować tych klas). Tak więc olbrzymswitch
w czasie wykonywania obiektów będzie Twoją ulubioną bronią.źródło
W Javie metoda do wywołania (jak w przypadku której sygnatury metody użyć) jest określana w czasie kompilacji, więc jest zgodna z typem czasu kompilacji.
Typowym sposobem obejścia tego problemu jest sprawdzenie typu obiektu w metodzie z podpisem Object i delegowanie do metody za pomocą rzutowania.
Jeśli masz wiele typów i jest to niemożliwe do zarządzania, to przeciążenie metod prawdopodobnie nie jest właściwym podejściem, a raczej metoda publiczna powinna po prostu przyjąć Object i zaimplementować pewien wzorzec strategii, aby delegować odpowiednią obsługę dla typu obiektu.
źródło
Miałem podobny problem z wywołaniem odpowiedniego konstruktora klasy o nazwie „Parameter”, która mogłaby przyjmować kilka podstawowych typów Java, takich jak String, Integer, Boolean, Long itp. Biorąc pod uwagę tablicę obiektów, chcę je przekształcić w tablicę moich obiektów Parameter, wywołując najbardziej specyficzny konstruktor dla każdego obiektu w tablicy wejściowej. Chciałem również zdefiniować parametr konstruktora (obiekt o), który będzie zgłaszał IllegalArgumentException. Oczywiście zauważyłem, że ta metoda jest wywoływana dla każdego obiektu w mojej tablicy.
Rozwiązaniem, którego użyłem, było wyszukanie konstruktora poprzez odbicie ...
Nie są wymagane żadne brzydkie instancje, instrukcje przełączania ani wzorce odwiedzających! :)
źródło
Java sprawdza typ referencyjny, próbując określić, którą metodę wywołać. Jeśli chcesz wymusić swój kod, wybierasz `` właściwą '' metodę, możesz zadeklarować swoje pola jako wystąpienia określonego typu:
Możesz również rzutować swoje parametry jako typ parametru:
źródło
Jeśli istnieje dokładne dopasowanie między liczbą i typami argumentów określonymi w wywołaniu metody a sygnaturą metody przeciążonej metody, to jest to metoda, która zostanie wywołana. Używasz odwołań do Object, więc java decyduje w czasie kompilacji, że dla parametru Object istnieje metoda, która akceptuje bezpośrednio Object. Więc wywołał tę metodę 3 razy.
źródło