Według Java Language Sepecification , 3. edycja:
Chciałbym zrozumieć, dlaczego podjęto taką decyzję. Co jest nie tak z ogólnymi wyjątkami?
(O ile wiem, typy generyczne są po prostu cukrem składniowym w czasie kompilacji i zostaną przetłumaczone na Object
takie .class
pliki w plikach, więc efektywne zadeklarowanie klasy ogólnej jest tak, jakby wszystko w niej było Object
. Popraw mnie, jeśli się mylę .)
java
generics
exception
language-design
Hosam Aly
źródło
źródło
myList.get(i)
, oczywiścieget
nadal zwracaObject
. Czy kompilator wstawia rzutowanie doA
, aby uchwycić część ograniczenia w czasie wykonywania? Jeśli nie, OP ma rację, że ostatecznie sprowadza się doObject
s w czasie wykonywania. (Plik klasy z pewnością zawiera metadane dotycząceA
, ale to tylko metadane AFAIK.)Odpowiedzi:
Jak powiedział Mark, typy nie są reifable, co jest problemem w następującym przypadku:
Oba
SomeException<Integer>
iSomeException<String>
są kasowane do tego samego typu, nie ma możliwości, aby maszyna JVM rozróżniła wystąpienia wyjątków, a zatem nie ma możliwości określenia, którycatch
blok powinien zostać wykonany.źródło
Oto prosty przykład użycia wyjątku:
Treść instrukcji TRy zgłasza wyjątek z daną wartością, który jest przechwytywany przez klauzulę catch.
Natomiast poniższa definicja nowego wyjątku jest zabroniona, ponieważ tworzy sparametryzowany typ:
Próba skompilowania powyższego zgłasza błąd:
To ograniczenie jest rozsądne, ponieważ prawie każda próba przechwycenia takiego wyjątku musi się nie powieść, ponieważ typu nie można ponowić. Można by się spodziewać, że typowe użycie wyjątku będzie wyglądać następująco:
Jest to niedozwolone, ponieważ typu w klauzuli catch nie można ponowić. W chwili pisania tego tekstu kompilator Sun zgłasza kaskadę błędów składniowych w takim przypadku:
Ponieważ wyjątki nie mogą być parametryczne, składnia jest ograniczona, więc typ musi być zapisany jako identyfikator, bez następującego parametru.
źródło
Dzieje się tak zasadniczo dlatego, że został zaprojektowany w zły sposób.
Ten problem uniemożliwia czysty abstrakcyjny projekt, np.
Fakt, że klauzula catch zawiodłaby dla typów ogólnych, nie jest reifikowana, nie jest dla tego usprawiedliwieniem. Kompilator może po prostu zabronić konkretnych typów ogólnych, które rozszerzają Throwable lub nie zezwalać na typy ogólne wewnątrz klauzul catch.
źródło
EntityNotFoundException
. Ale to uczyniłoby generyczne bezużytecznymi.Typy generyczne są sprawdzane w czasie kompilacji pod kątem poprawności typu. Informacje o typie ogólnym są następnie usuwane w procesie zwanym wymazywaniem typu . Na przykład
List<Integer>
zostanie przekonwertowany na typ nieogólnyList
.Ze względu na wymazywanie typu nie można określić parametrów typu w czasie wykonywania.
Załóżmy, że możesz rozszerzyć w
Throwable
ten sposób:Rozważmy teraz następujący kod:
Ze względu na wymazywanie typu środowisko wykonawcze nie będzie wiedział, który blok catch wykonać.
Dlatego jest to błąd czasu kompilacji, jeśli klasa ogólna jest bezpośrednią lub pośrednią podklasą Throwable.
Źródło: problemy z wymazywaniem typu
źródło
Spodziewałbym się, że to dlatego, że nie ma możliwości zagwarantowania parametryzacji. Rozważ następujący kod:
Jak zauważyłeś, parametryzacja to tylko cukier syntaktyczny. Jednak kompilator próbuje zapewnić spójność parametryzacji we wszystkich odwołaniach do obiektu w zakresie kompilacji. W przypadku wyjątku kompilator nie ma możliwości zagwarantowania, że MyException jest wyrzucany tylko z zakresu, który przetwarza.
źródło