Почему я не могу переопределить метод, используя реализацию класса в качестве параметра - PullRequest
3 голосов
/ 06 июня 2019

У меня простая абстрактная структура

public abstract class Data<A extends Serializable> {

}

и затем String реализации этого класса

public class StringData extends Data<String> {

}

тогда у меня есть интерфейс:

public interface Testicek<A extends Serializable> {

    public abstract Data<A> test(Data<A> bla);

}

и теперь я хочу создать класс, который реализует этот интерфейс:

public class TesticekImpl implements Testicek<String> {


    // OK
    @Override
    public StringData test(Data<String> bla) {
        return null;
    }

    // compilation error
    //@Override
    //public StringData test(StringData bla) {
    //    return null;
    //}

}

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

1 Ответ

5 голосов
/ 06 июня 2019
public interface Testicek<A extends Serializable> {

    public abstract Data<A> test(Data<A> bla);

}

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

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

Принцип подстановки Лискова говорит нам, что подклассы должны принимать параметры, которые не являются более ограничительными, и должны возвращать значения, которые не являются более общими.

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

Если вы принимаете и возвращаете один и тот же тип: объявите переменную другого типа в вашем интерфейсе:

public interface Testicek<A extends Serializable, D extends Data<A>> {

    public abstract D test(D bla);

}

Тогда ваша реализация может быть:

public class TesticekImpl implements Testicek<String, StringData> {
    @Override
    public StringData test(StringData bla) {
        return null;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...