Polimorfizm to zasada programowania obiektowego mówiąca o tym, że
A. ta sama metoda, zdefiniowana w klasie bazowej jako wirtualna, może mieć różne implementacje w klasach pochodnych.
B. typy pól w klasach/obiektach mogą być dynamicznie zmieniane w zależności od danych im przypisywanych.
C. pola i metody wykorzystywane tylko przez daną klasę/obiekt są ograniczane zasięgiem private lub protected.
D. funkcja może wywoływać samą siebie.
Polimorfizm w programowaniu obiektowym oznacza, że obiekt może być traktowany przez typ bazowy, a konkretne zachowanie zostanie dobrane dopiero na podstawie rzeczywistej klasy obiektu. W praktyce chodzi właśnie o sytuację, w której metoda zadeklarowana w klasie bazowej jako wirtualna, abstrakcyjna albo wynikająca z interfejsu, ma różne implementacje w klasach pochodnych. Na przykład klasa bazowa Figura może mieć metodę obliczPole(), a klasy Koło, Prostokąt i Trójkąt wykonają ją inaczej. Kod korzystający z listy obiektów typu Figura nie musi wiedzieć, z jaką figurą dokładnie pracuje. Po prostu wywołuje obliczPole(), a mechanizm dynamicznego wiązania wybiera właściwą wersję metody. To jest bardzo praktyczne, bo zmniejsza liczbę instrukcji warunkowych, poprawia czytelność i ułatwia rozbudowę programu. Z mojego doświadczenia to jedna z tych zasad OOP, która na początku brzmi trochę teoretycznie, ale potem naprawdę pomaga pisać czystszy kod. W dobrych praktykach branżowych polimorfizm łączy się często z interfejsami, klasami abstrakcyjnymi, zasadą podstawienia Liskov oraz projektowaniem kodu otwartego na rozszerzanie, ale zamkniętego na ciągłe poprawianie istniejących fragmentów.
W tym pytaniu łatwo pomylić kilka różnych pojęć z programowania obiektowego i ogólnych podstaw programowania. Ograniczanie dostępu do pól oraz metod za pomocą private albo protected dotyczy hermetyzacji, nazywanej też enkapsulacją. To ważna zasada OOP, bo pozwala ukrywać szczegóły implementacji klasy i chronić obiekt przed przypadkową, niekontrolowaną zmianą stanu. Nie jest to jednak polimorfizm. Polimorfizm nie mówi o tym, kto ma dostęp do pola, tylko o tym, że ten sam komunikat, czyli wywołanie metody, może dawać różne zachowanie zależnie od typu obiektu. Mylenie tych pojęć jest dość typowe, bo oba tematy często pojawiają się razem przy klasach. Dynamiczna zmiana typów pól to z kolei zagadnienie związane raczej z typowaniem dynamicznym, spotykanym np. w JavaScript czy PHP. W takich językach zmienna może raz przechowywać liczbę, a potem tekst, ale to nie jest definicja polimorfizmu obiektowego. Moim zdaniem to taka pułapka językowa: brzmi jak elastyczność, więc ktoś może skojarzyć ją z polimorfizmem, ale technicznie chodzi o coś innego. Wywoływanie funkcji przez samą siebie to rekurencja. Rekurencja przydaje się np. przy przechodzeniu po drzewach, katalogach albo przy klasycznych algorytmach, lecz nie wymaga dziedziczenia, metod wirtualnych ani klas pochodnych. Poprawne rozumienie jest takie: klasa bazowa definiuje wspólny interfejs zachowania, a klasy pochodne dostarczają własne implementacje tej samej metody. Dzięki temu kod jest bardziej rozszerzalny i zgodny z dobrymi praktykami projektowania obiektowego.