Возможно ли в PHP заменить parent дочерними элементами в свойстве класса?

#php #oop

#php #ооп

Вопрос:

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

Вот очень упрощенный пример:

 class A {
    public int $x;
}

class B extends A {
    public int $y;
}

class C {
    public A $ca;
}

class D extends C {
    public B $ca;
}

$vd = new D();
$vd->cd->x = 10;

echo $vd->cd->x;
  

Ошибка: тип D::$ca должен быть A (как в классе C)

Классы A и C находятся в библиотеке и классах B и D переопределяют их.

Комментарии:

1. У меня никогда не было такой проблемы, но чтение сообщения об ошибке, вероятно, невозможно. Вы могли бы сделать атрибут закрытым и предоставить доступ только через методы getter и setter . Таким образом, вы можете принудительно установить, соответствует ли атрибут вашим критериям. Что бы вы ни пытались сделать, должен быть лучший способ. Возможно, мы могли бы помочь вам, если бы вы могли уточнить, зачем вам это нужно.

2. Примечание: вы вызываете $vd->cd в своем примере, но $cd не определено ни в одном из классов. Вы имели в виду использовать ca вместо cd ?

3. На практике классы A и C являются частью библиотеки. Класс A является фабричным, а класс B — одноэлементным. Мне нужно заменить (расширить) класс B на класс D. Этого можно достичь, перегрузив метод get класса A классом C. Затем все приложение (как библиотека, так и приложение) использует класс D вместо B. Это работает хорошо, но в классах A и C тип не должен указываться для свойства, содержащего экземпляр класса C (D). Затем он записывает ошибку, упомянутую выше.

4. Если это свойство является частным, код библиотеки будет использовать экземпляр класса B, а приложение — экземпляр класса D. Следовательно, это не синглтон!

Ответ №1:

Типизированные свойства инвариантны в PHP.

Из исходного RFC для типизированных свойств:

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

Причина, по которой типы свойств инвариантны, заключается в том, что они могут быть как прочитаны, так и записаны. Изменение с int на ?int подразумевает, что чтение из свойства теперь может также возвращать null в дополнение к целым числам. Изменение с ?int на int подразумевает, что больше невозможно записать null в свойство. Таким образом, ни контравариантность, ни ковариация не применимы к типам свойств.

Что вы можете сделать безопасно, так это использовать экземпляр B везде, где ожидался экземпляр A :

 
class C {
    public A $ca;
}

$c = new C();
$c->ca = new B();
  

… таким образом, выполняется принцип подстановки Лискова, согласно которому «если S является подтипом T, то объекты типа T могут быть заменены объектами типа S»