Mechanizm covariant return types pozwala w Javie podczas nadpisywania metody (overriding) zwracać nie dokładnie ten sam typ, co w superklasie, ale jego podtyp (covariant type).
To zwiększa ekspresyjność kodu, pozwalając uzyskiwać bardziej konkretne typy bez jawnego rzutowania.
Przykład:
class Animal { } class Dog extends Animal { } class Parent { Animal getAnimal() { return new Animal(); } } class Child extends Parent { @Override Dog getAnimal() { return new Dog(); } // Który zwraca kowariantny }
Warunki działania:
Czy można w podklasie zmienić typ zwracany w metodzie przeciążenia (overloaded method), aby stał się on podtypem klasy bazowej? Czy zostanie to uznane za prawidłowe przeciążenie?
Odpowiedź: Nie, w przypadku przeciążania (overloading) ważna jest tylko sygnatura po parametrach, a typ zwracany nie ma znaczenia. Zmieniać tylko typ zwracany w przeciążeniu nie można — taka metoda będzie kolidować co do sygnatury.
Przykład:
class Example { Animal make() { return new Animal(); } // Dog make() { return new Dog(); } // Błąd kompilacji: podwójna metoda! }
Kowariantność działa tylko dla overriding (nadpisywania).
Historia
Na projekcie programista pomylił się i dodał przeciążoną metodę o tej samej nazwie i parametrach, ale z innym typem zwracanym, spodziewając się, że to "kowariantne nadpisanie". W rezultacie projekt nie kompilował się, a błąd został wykryty dopiero w CI.
Historia
Podczas używania kowariantnego zwrotu w hierarchii klas, programista niewłaściwie zastosował typy parametryzowane (generics), nie implementując poprawnie podtypów, co spowodowało błąd ClassCastException w trakcie wykonywania przy pracy z kolekcjami.
Historia
W nadpisywanej metodzie zwracany był bardziej wyspecjalizowany typ, ale zespół nie udokumentował tego kontraktu. W kodzie działającym przez typ bazowy wystąpiły trudności z rzutowaniem typów i nieoczekiwane zachowanie podczas "upcast" referencji, co doprowadziło do wielu ukrytych błędów.