Java: переопределение абстрактного метода в подклассе

Я действительно должен это знать, но почему-то я не понимаю следующее.

мой абстрактный класс содержит следующий абстрактный метод:

protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);

у меня также есть другой класс следующим образом:

EvaluationRuleDTO extends from RuleDTO

тогда в подклассе моего абстрактного класса у меня есть следующая реализация, которая не разрешена из-за"должен переопределить или реализовать метод supertype":

protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {

однако, следующее разрешено:

protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {

Я понимаю, что это, вероятно, основной вопрос, но я немного озадачен. Почему я могу вернуть подкласс RuleDTO в переопределенном методе, но я не могу передать подкласс?

спасибо

6 ответов


вы нарушаете принцип Лискова: все, что может сделать суперкласс, должен быть в состоянии сделать подкласс. Суперкласс объявляет метод, принимающий любой тип RuleDTO. Но в вашем подклассе вы принимаете только экземпляры EvaluationRuleDTO. Что произойдет, если вы сделаете следующее?

RuleDTO rule = new EvaluationRuleDTO();
rule.createRowToBeCloned(new RuleDTO());

EvaluationRuleDTO является RuleDTO, поэтому он должен выполнять контракт, определенный RuleDTO.

метод в подклассе может возвращать экземпляр EvaluationRuleDTO вместо однако RuleDTO, потому что контракт должен вернуть RuleDTO, а EvaluationRuleDTO-это RuleDTO.


Java разрешает ковариацию возвращаемого типа для переопределений, поэтому вы можете указать возвращаемый тип переопределения как более производный тип. Однако Java не разрешает ковариацию типа параметра для переопределений.

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

Это не относится к аргументам, хотя. Если бы это было законно, пользователь мог бы вызвать абстрактный метод и передать менее производный тип (так как это тип, объявленный в абстрактном классе), но тогда ваше производное переопределение может попытаться получить доступ к аргументу как более производному типу (которого нет), что приводит к ошибке.

теоретически Java могла бы разрешить параметр-type contra-дисперсия, так как это типобезопасно: если переопределенный метод ожидает только меньше-производный аргумент, вы не можете случайно использовать метод или поле, которого нет. К сожалению, в настоящее время такой возможности нет.


Это потому, что при переопределении метода вы должны использовать в качестве типа параметра тот же тип или более общий (более широкий) тип, никогда не более узкий тип.

просто подумай об этом. Если бы вы могли переопределить метод, используя более узкий тип, вы бы нарушили способность полиморфизма, не так ли? Таким образом, делая это, вы нарушите Принцип Подстановки Лисков как JB Nizet сказал в своем ответе.


в Java 1.5 есть co-variant return типы, которые почему это действительно

возвращаемый тип R2 метода подкласса может отличаться от суперкласса метод возвращает тип R1, но R2 должен быть подтипом R1. т. е., подкласс может возвращать тип может быть подтипом типа возврата суперкласса.


в ранней java это было не так, но оно было изменено в Java 5.0.

вы не можете иметь два метода в одном классе с подписями, которые отличаются только типом возвращаемого значения. До выпуска J2SE 5.0 также было верно, что класс не может переопределить возвращаемый тип методов, которые он наследует от суперкласса. В этом совете вы узнаете о новой функции в J2SE 5.0, которая позволяет ковариантные возвращаемые типы. Это означает, что метод в подклассе может возвращать объект, тип которого является подклассом типа, возвращаемого методом с той же подписью в суперклассе. Эта функция устраняет необходимость чрезмерной проверки типа и литья.

источник:http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html

это означает, что возвращаемые типы переопределяющих методов будут подтипами возвращаемого типа переопределенного метода.


пожалуйста, см. следующий код:

class A {
    A foo(A a) {
        return new A();
    }
}

class B extends A {
    @Override
    // Returning a subtype in the overriding method is fine,
    // but using a subtype in the argument list is NOT fine!
    B foo(B b) {
        b.bar();
        return new B();
    }
    void bar() {
        // B specific method!
    }
}

Да ОК, B-это A, но что произойдет, если кто-то сделает:

B b = new B();
b.foo(new A());

a не имеет метода bar.. Вот почему аргумент не может быть подтипом типа аргумента в переопределяемом методе.

возврат A или B в метод переопределения в порядке. Следующий фрагмент будет скомпилирован и запущен просто отлично..

class A {
    A foo(A a) {
        return new B(); // B IS AN A so I can return B!
    }
}

class B extends A {
    @Override
    B foo(A b) {
        return new B(); // Overridden method returns A and 
                        // B IS AN A so I can return B!
    }

    public static void main(String[] args) {
        A b = new B();
        final A foo = b.foo(new B());
        // I can even cast foo to B!
        B cast = (B) foo;
    }
}