Я просто изучаю Java, а не практикующий программист.
В книге, за которой я следую, говорится, что при переопределении метода типы аргументов должны быть одинаковыми, но возвращаемые типы могут быть полиморфно совместимы.
Мой вопрос : почему аргументы, передаваемые переопределяющему методу, не могут быть типом подкласса ожидаемого супертипа?
В перегруженном методе любой метод, который я вызываю для объекта, гарантированно будет определен для объекта.
Примечания по предлагаемым дубликатам:
Первое предложение , как представляется, о классовой иерархии и где поставить функциональность. Мой вопрос больше сфокусирован на том, почему существуют языковые ограничения.
Второе предложение объясняет , как сделать то , что я прошу, но не почему это должно быть сделано таким образом. Мой вопрос сосредоточен на том, почему.
источник
Ответы:
Концепция, на которую вы изначально ссылаетесь в своем вопросе, называется ковариантными типами возврата .
Ковариантные возвращаемые типы работают, потому что метод должен возвращать объект определенного типа, а переопределяющие методы могут фактически возвращать его подкласс. Основываясь на правилах подтипирования языка, такого как Java, если
S
это подтипT
, то где быT
мы ни появлялись, мы можем передатьS
.Таким образом, безопасно возвращать
S
при переопределении метода, который ожидал aT
.Ваше предложение согласиться с тем, что переопределяющий метод использует аргументы, являющиеся подтипами аргументов, запрашиваемых переопределенным методом, намного сложнее, поскольку приводит к несостоятельности в системе типов.
С одной стороны, с помощью тех же правил подтипов, упомянутых выше, скорее всего, это уже работает для того, что вы хотите сделать. Например
Ничто не мешает реализациям этого класса получать любое животное, так как оно уже удовлетворяет критериям вашего вопроса.
Но давайте предположим, что мы можем переопределить этот метод, как вы предложили:
Вот забавная часть, теперь вы можете сделать это:
Согласно общедоступному интерфейсу
AnimalHunter
вы должны быть в состоянии охотиться на любое животное, но согласно вашей реализацииMammutHunter
вы принимаете толькоMammut
объекты. Поэтому переопределенный метод не удовлетворяет общедоступному интерфейсу. Мы только что нарушили надежность системы типов здесь.Вы можете реализовать то, что вы хотите, используя дженерики.
Тогда вы можете определить свой MammutHunter
А используя общую ковариацию и контравариантность, вы можете при необходимости ослабить правила в свою пользу. Например, мы могли бы убедиться, что охотник на млекопитающих может охотиться только на кошек в заданном контексте:
Предположим,
MammalHunter
орудияAnimalHunter<Mammal>
.В этом случае это не будет принято:
Даже если мамонты являются млекопитающими, это не будет принято из-за ограничений на контравариантный тип, который мы здесь используем. Таким образом, вы все еще можете получить некоторый контроль над типами, чтобы делать вещи, подобные тем, которые вы упомянули.
источник
Проблема не в том, что вы делаете в переопределенном методе с объектом, заданным в качестве аргумента. Проблема в том, какой тип аргументов разрешено передавать коду с помощью вашего метода.
Переопределяющий метод должен выполнять контракт переопределенного метода. Если переопределенный метод принимает аргументы некоторого класса, и вы переопределяете его методом, который принимает только подкласс, он не выполняет контракт. Вот почему это не действует.
Переопределение метода с более конкретным типом аргумента называется перегрузкой . Он определяет метод с тем же именем, но с другим или более конкретным типом аргумента. Перегруженный метод доступен помимо оригинального метода. Какой метод вызывается, зависит от типа, известного во время компиляции. Чтобы перегрузить метод, вы должны удалить аннотацию @Override.
источник