Jeśli spróbuję rzucić String
ajava.util.Date
, kompilator Java wyłapuje błąd. Dlaczego więc kompilator nie oznacza następującego błędu jako błędu?
List<String> strList = new ArrayList<>();
Date d = (Date) strList;
Oczywiście JVM rzuca ClassCastException
w czasie wykonywania, ale kompilator go nie oflaguje.
Zachowanie jest takie samo w przypadku javac 1.8.0_212 i 11.0.2.
java
casting
compiler-errors
javac
Mike Woinoski
źródło
źródło
List
tu nic specjalnego .Date d = (Date) new Object();
strList
była instancją klasy, która implementuje List.Odpowiedzi:
Obsada jest technicznie możliwa. Javac nie może łatwo udowodnić, że tak nie jest w twoim przypadku, a JLS faktycznie definiuje to jako prawidłowy program Java, więc oznaczenie błędu byłoby nieprawidłowe.
To dlatego, że
List
jest interfejsem. Więc możesz mieć podklasę,Date
która faktycznie implementuje się wList
przebraniu, tak jakList
tutaj - a następnie rzutowanie naDate
byłoby całkowicie w porządku. Na przykład:I wtedy:
Wykrywanie takiego scenariusza może nie zawsze być możliwe, ponieważ wymagałoby to informacji o środowisku wykonawczym, jeśli instancja pochodzi na przykład z metody. I nawet jeśli kompilator wymagałby znacznie więcej wysiłku. Kompilator zapobiega tylko rzutowaniom, które są absolutnie niemożliwe z powodu braku możliwości dopasowania drzewa klas. Jak widać, w niniejszym przypadku tak nie jest.
Pamiętaj, że JLS wymaga, aby kod był prawidłowym programem Java. W 5.1.6.1. Dozwolona zawężająca się konwersja odniesienia mówi:
Więc nawet jeśli kompilator może zorientować się, że twoja sprawa jest rzeczywiście niemożliwa do udowodnienia, nie jest dozwolone oznaczenie błędu, ponieważ JLS definiuje go jako prawidłowy program Java.
Dopuszczalne byłoby jedynie wyświetlenie ostrzeżenia.
źródło
myDate = (Date) myString
zawodzi. Za pomocą terminologii JLS instrukcja próbuje przekonwertować zS
(theString
) naT
(theDate
). TutajS
nie ma typu interfejsu, więc powyższy warunek JLS nie ma zastosowania. Jako przykład spróbuj rzucić kalendarz na datę, a otrzymasz błąd kompilatora, nawet jeśli żadna klasa nie jest ostateczna.Date & List
jest do zamieszkania , to nie wystarczy, aby udowodnić, że jest niezamieszkane obecnie (może to być w przyszłości).Rozważmy uogólnienie twojego przykładu:
Są to główne powody, dla których
Date d = (Date) strList;
nie występuje błąd kompilacji.Intuicyjny powodem jest to, że kompilator nie (w ogóle) zna dokładny typ obiektu zwróconego przez wywołanie tej metody. Możliwe, że oprócz tego
List
, że jest klasą, która implementuje , jest to również podklasaDate
.Przyczyną techniczną jest to, że specyfikacja języka Java „pozwala” na zawężenie konwersji referencji , który odpowiada na tego typu cast. Zgodnie z JLS 5.1.6.1 :
W innym miejscu JLS mówi również, że wyjątek może zostać zgłoszony w czasie wykonywania ...
Należy zauważyć, że określenie JLS 5.1.6.1 opiera się wyłącznie na zadeklarowanych typach zmiennych, a nie na rzeczywistych typach środowiska wykonawczego. W ogólnym przypadku kompilator nie zna i nie może znać rzeczywistych typów środowiska wykonawczego.
Dlaczego więc kompilator Java nie może się zorientować, że obsada nie działa?
W moim przykładzie
someMethod
wywołanie może zwrócić obiekty różnego rodzaju. Nawet jeśli kompilator był w stanie przeanalizować treść metody i określić dokładny zestaw typów, które mogą zostać zwrócone, nic nie stoi na przeszkodzie, aby ktoś zmienił go, aby zwracał różne typy ... po skompilowaniu kodu, który go wywołuje. Jest to podstawowy powód, dla którego JLS 5.1.6.1 mówi to, co mówi.W twoim przykładzie inteligentny kompilator może stwierdzić, że rzutowanie nigdy się nie powiedzie. I wolno emitować ostrzeżenie podczas kompilacji, aby wskazać problem.
Dlaczego więc inteligentny kompilator nie może powiedzieć, że to błąd?
Ponieważ JLS mówi, że jest to poprawny program. Kropka. Kompilator, który nazwałby to błędem , nie byłby zgodny z Javą.
Ponadto każdy kompilator, który odrzuca programy Java, które zdaniem JLS i innych kompilatorów są poprawne, stanowi przeszkodę w przenoszeniu kodu źródłowego Java.
źródło
5.5.1 Typ odniesienia Casting:
List<String>
jestS
iDate
jestT
w twoim przypadku.źródło