Motywacja (zgodność wsteczna) jest zarówno zaletą, jak i wadą. Jest to niekorzystne, ponieważ wszyscy wolelibyśmy mieć typy podlegające zwrotowi, ale cena do zapłaty była wysoka. Rozważ wybór projektu w języku C #. Mają zmienne typy, ale teraz mają zduplikowane interfejsy API. Wyobraźmy sobie API Java, w którym mieliśmy również zduplikowane interfejsy API dla każdej sparametryzowanej klasy. Teraz wyobraź sobie, jak przenosisz tysiące wierszy kodu ze starszych klas do nowych klas ogólnych. Kto nie uznałby duplikatów interfejsów API za wadę? Ale hej, mają typy, które można zmienić!
Tak więc główną motywacją była „ewolucja, a nie rewolucja”. I logicznie, każda decyzja ma kompromisy.
Oprócz innych wspomnianych wad możemy dodać fakt, że usunięcie typu może być trudne do uzasadnienia w czasie kompilacji, ponieważ nie jest oczywiste, że niektóre typy zostaną usunięte, a to prowadzi do bardzo dziwnych i trudnych do znalezienia błędów.
Istnienie metod pomostowych (ten kompilator generowany syntaktycznie w celu zachowania zgodności binarnej) można również postrzegać jako wadę. I może to być właśnie jeden z powodów błędów, o których wspomniałem w poprzednim akapicie.
Główne wady wynikają z już oczywistego faktu, że istnieje jedna klasa, a nie wiele klas dla typów ogólnych. Jako kolejny przykład rozważ, że przeciążenie metody tą samą klasą ogólną kończy się niepowodzeniem w Javie:
public void doSomething(List<One>);
public void doSomething(List<Two>);
Coś, co może być postrzegane jako wada typów wielokrotnego użytku (przynajmniej w języku C #), to fakt, że powodują one eksplozję kodu . Na przykład List<int>
jest jedna klasa, a a List<double>
jest zupełnie inna, ponieważ jest to a List<string>
i List<MyType>
. Tak więc klasy muszą być zdefiniowane w czasie wykonywania, powodując eksplozję klas i pochłaniając cenne zasoby podczas ich generowania.
Biorąc pod uwagę fakt, że nie jest możliwe zdefiniowanie new T()
w Javie, o którym mowa w innej odpowiedzi, interesujące jest również to, że nie jest to tylko kwestia usunięcia typu. Wymaga również istnienia domyślnego konstruktora, dlatego C # wymaga do tego „nowego ograniczenia”. (Zobacz Dlaczego nowa T () nie jest możliwa w Javie , autor: Alex Buckley).
T
, otrzymujesz tylko jedną kopięClass<T>
kodu dla wszystkichT
s; plus dodatkowa kopia dla każdego faktycznie użytego typu wartościT
.Minusem kasowania typu jest to, że nie znasz w czasie wykonywania typu ogólnego. Oznacza to, że nie można na nich zastosować refleksji i nie można ich utworzyć w środowisku wykonawczym.
Nie można zrobić czegoś takiego w Javie:
Istnieje obejście tego problemu, ale wymaga ono więcej kodu. Zaletą, jak już wspomniałeś, jest kompatybilność wsteczna.
źródło
Kolejną zaletą usuniętych danych ogólnych jest to, że różne języki kompilujące się do JVM stosują różne strategie dla danych ogólnych, np. Strona definicji Scali kontra kowariancja strony użycia Javy. Także bardziej rodzajowe generały Scali byłyby trudniejsze do obsługi na zmienionych typach .Net, ponieważ zasadniczo Scala na .Net zignorował niekompatybilny format potwierdzania C #. Gdybyśmy zreformowali generyczne w JVM, najprawdopodobniej te zreifikowane generyczne nie byłyby odpowiednie dla funkcji, które naprawdę lubimy w Scali, i utknęlibyśmy z czymś nieoptymalnym. Cytowanie z bloga Oli Bini ,
Osobiście nie uważam konieczności używania
TypeTag
kontekstu związanego ze Scalą w przypadku sprzecznych przeciążonych metod za wadę, ponieważ przenosi on narzut i brak elastyczności reifikacji z globalnego (całego programu i wszystkich możliwych języków) na stronę użytkowania dla każdego języka problem występuje tylko rzadziej.źródło