Почему типы параметров не могут быть ослаблены в переопределенных методах?

Этот код недействителен:

interface Foo
{
    public void foo(final String string);
}

public class Bar implements Foo
{
    // Error: does not override.
    @Override public void foo(final Object object)
    {
    }
}

Потому что каждый String очевидно, ObjectЯ бы ожидал, что этот код будет в порядке: любой код, который зависит от предоставления foo() String будет работать, когда foo() на самом деле занимает Object,

Похоже, однако, что сигнатуры методов должны быть идентичны тем из методов, которые они переопределяют. Зачем?

4 ответа

Решение

Что, если

interface Foo
{
    void foo(String string);
    void foo(Object string);
}

Тогда какой метод переопределяется Bar?

Это просто конструкции языка программирования Java. Структура программ Java будет расти на вас. Так что сейчас просто попробуйте и отрегулируйте.

"ослабление", как вы говорите, не должно влиять на того, кто ожидает использовать ваш интерфейсный метод определенным образом, так как любые методы, которые вы вызываете для этого объекта, должны теоретически вызываться для указанного объекта, но точка зрения Юджина стоит, потому что это будет возможно, вам придется немного разболтаться при определении того, какой метод вы на самом деле намереваетесь переопределить, если вы просто хотите придерживаться спецификации интерфейсов. Кроме того, почему это может быть желательным, если вы собираетесь придерживаться иерархии наследования, потому что, несомненно, вы сможете делать все, что хотите, с вещами ниже по иерархии, например, с "Объектом"? Возможно, литье внутри вашего метода решит вашу проблему? Если возможно сделать то, что вы хотите сделать, вы также, вероятно, начнете наступать на парадигму полиморфизма.

Я думаю, что это классическая проблема контравариантности. Ваш интерфейс требует, чтобы в качестве параметра была передана строка, вам нужна реализация, которая принимает объект (потому что, в конце концов, строки также являются объектами).

Проблема заключается в том, что если вы позволите это, то вы больше не сможете гарантировать, что параметр, требуемый интерфейсом, является строкой или любым из его предков. С тем же успехом вы можете передать любой объект вашей реализации, и вы нарушите контракт интерфейса, подвергая опасности безопасность типов и согласованность типов вашего проекта.

У вас есть варианты, хотя:

public class Bar implements Foo
{

    @Override public void foo(final String object)
    {
    }

    public void foo(final Object object)
    {
       foo((String) object);
    }
}

Таким образом, вы гарантируете, что объект на самом деле является строкой, что позволит системе типов проверить, что вы действительно соблюдаете контракт интерфейса, установленный в сигнатуре метода.

Есть ли конкретный сценарий, в котором вы бы рассматривали свой пример контравариантности как требование?

Другие вопросы по тегам