Ciekawe dlaczego? Mamy kilka tropów.
Java bardziej rygorystyczna
Java została zaprojektowana z regułami ograniczającymi swobodę programisty, narzucając wybory i strukturę. Python ma zupełnie inną filozofię, pozostawiając programiście wolność wyboru ale i ewentualną odpowiedzialność za naruszenie zasad pisania projektu. Oto niektóre z najbardziej irytujących ograniczeń, których możesz doświadczyć w przypadku Javy jako programista Pythona przyzwyczajony do oferowanej przez niego swobody.
Dobry, zły i brzydki czyli private, public i protected
Java ma zestaw kwalifikatorów pozwalających programiście decydować czy można uzyskać dostęp do zmiennej, metody, a nawet klasy. Ograniczanie dostępu do metod i zmiennych to jednak także swego rodzaju kajdanki nakładane na użytkownika.
Czy zakazanie dostępu do określonej dziedziny lub metody na pewno zapobiegnie niewłaściwemu użyciu danego kodu? Czy wiedza o tym nie byłaby wystarczająca, bez potrzeby sięgania po daleko idące restrykcje? Kiedy już zdam sobie sprawę, że z daną metodą czy klasą nie powinno się zadzierać (czytaj: modyfikować jej zawartości) to i tak mogę się zdecydować na zrobienie tego. Jeśli moje oprogramowanie przestanie działać, jestem jedyną winną temu osobą. Jeśli chcę, aby dane pole z klasy nie było dziedziczone, mam wyjątek od dziedziczenia - i bez tego mógłbym się obejść.
Python ma konwencje oznaczania pól, metod lub klas prywatnego dostępu. Masz świadomość, że uzyskujesz do nich dostęp w nieoczekiwany sposób. Możesz chcieć znaleźć inne rozwiązanie, ale jeśli wiesz, co robisz, po prostu możesz sięgnąć po „zakazany owoc”.
Gadatliwość
Java to szczegółowość i duża liczba wierszy, które programista musi napisać dla powtarzających się zadań, z niewielkimi lub żadnymi szczególnymi zmianami. Java jest jednym z bardziej „rozgadanych” języków programowania, wymagającym – dla zrobienia praktycznie czegokolwiek - wielu linijek kodu. A to jest dość wyczerpujące.
Oczywiście są sposoby obejścia tego problemu (za pomocą automatycznie generowanego kodu, adnotacji itp.) ale czasami można pomyśleć: „Hej, po prostu wstawiłem tam zmienną. Dlaczego mam napisać settera i gettera, aby mieć do niego bezpieczny dostęp?”. Dlaczego miałbym w pełni zaprojektować strukturę podobną do klasy (która nie jest klasą, ale interfejsem) tylko po to, aby zadeklarować strukturę konkretnej implementacji, którą zamierzam umieścić w innym miejscu? To raczej skomplikowanie prostego tematu, czy na pewno potrzebne?
Cóż, oczywiście, interfejsy mają swoją logikę i powód, dla którego są w Javie. Ale trzykrotne pisanie tych samych sygnaturek i tworzenie dwóch implementacji struktury jest czasochłonne. Python jest często schludniejszy i wymaga mniejszej liczby wierszy do wykonania tego samego zadania.
Wielokrotne dziedziczenie i składnia
Java ma dziedziczenie wielokrotne oparte na interfejsie. Zmusza to programistę do umieszczenia kolejnej warstwy abstrakcji zawierającej wspólną definicję przeznaczoną do zaimplementowania. A to jest przeciwieństwem prostoty i pragmatyzmu. Zasady pisania w Javie są surowe, a sygnatury metod, typy ogólne, interfejsy, klasy abstrakcyjne i rzutowanie mogą być problemem zarówno przy prawidłowym projektowaniu, jak i przy debugowaniu.
Python ma składnię, która pozwala uniknąć tych problemów a przy tym także pozwala na wielokrotne dziedziczenie. Nazywa się to kaczym typowaniem (duck typing). Mówiąc w skrócie: dopóki instancja obiektu kwacze, możesz uważać ją za kaczkę. Oznacza to, że nie ma potrzeby sprawdzania typu, o ile żądania danego bloku kodu są spełnione.
Sprawdź oferty pracy na TeamQuest
Tyle tego…
Java ma typy i klasy definiujące co jest czym. Ale co to jest klasa? Co to jest typ? Funkcja? Metoda? Abstrakcyjne rzeczy i magia dziejąca się za kulisami sprawiają, że finalnie to konstruktor tworzy instancję obiektu. Python ma dwie bardzo proste odpowiedzi na wszystkie te pytania: wszystko jest obiektem, a wszystkie metody klasy działają jak statyczne metody Java.
Oznacza to, że z wyjątkiem typów podstawowych wszystkie inne jednostki są instancjami klas. Ponadto definicje typów są klasami, a ich instancje są obiektami. Funkcje, metody i wyrażenia lambda również są obiektami. Rzeczywiste metody statyczne są opatrzone adnotacją @staticmethod i nie mają argumentu własnego.