Концепція, на яку ви спочатку посилаєтесь у своєму запитанні, називається коваріантними типами повернення .
Коваріантні типи повернення працюють, оскільки метод повинен повертати об'єкт певного типу, а переосмислюючі методи можуть фактично повернути його підклас. Виходячи з правил S
підтипу такої мови, як Java, якщо це підтип T
, тоді, де б не T
з'явилося, ми можемо передати S
.
Як такий, можна безпечно повернути знак S
при перекритті методу, який очікував a T
.
Ваша пропозиція визнати, що метод, що переосмислює, використовує аргументи, що є підтипами тих, що запитуються методом переосмисленості, набагато складніше, оскільки це призводить до нерівномірності в системі типів.
З одного боку, за тими ж вищезазначеними правилами підтипів, швидше за все, це вже працює для того, що ви хочете зробити. Наприклад
interface Hunter {
public void hunt(Animal animal);
}
Ніщо не заважає реалізаціям цього класу отримувати будь-яку тварину, тому що вона вже відповідає критеріям у вашому питанні.
Але припустимо, що ми могли б замінити цей метод, як ви запропонували:
class MammutHunter implements Hunter {
@Override
public void hunt(Mammut animal) {
}
}
Ось найсмішніша частина, тепер ви могли це зробити:
AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh
Згідно з загальнодоступним інтерфейсом AnimalHunter
ви повинні мати можливість полювати на будь-яку тварину, але відповідно до вашої реалізації MammutHunter
ви приймаєте лише Mammut
об'єкти. Тому метод перекриття не задовольняє публічний інтерфейс. Тут ми просто порушили надійність системи типів.
Ви можете реалізувати те, що хочете, використовуючи дженерики.
interface AnimalHunter<T extends Animal> {
void hunt(T animal);
}
Тоді ви могли б визначити свого MammutHunter
class MammutHunter implements AnimalHunter<Mammut> {
void hunt(Mammut m){
}
}
І використовуючи загальну коваріантність та протиріччя, ви можете розслабити правила на свою користь, коли це необхідно. Наприклад, ми могли переконатися, що мисливець на ссавців може полювати тільки на котячих у заданому контексті:
AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());
Припущення MammalHunter
знарядь AnimalHunter<Mammal>
.
У такому випадку це не буде прийнято:
hunter.hunt(new Mammut()):
Навіть коли ссавці є ссавцями, це не було б прийнято через обмеження щодо противаріантного типу, який ми тут використовуємо. Отже, ви все ще можете здійснити деякий контроль над типами, щоб робити такі речі, як ті, які ви згадали.