Vamos ignorar por um segundo que o método em questão é __construct
e chamá-lo frobnicate
. Agora, suponha que você tenha um objeto api
implementado IHttpApi
e um objeto config
implementado IHttpConfig
. Claramente, esse código se encaixa na interface:
$api->frobnicate($config)
Mas vamos supor que upcast api
para IApi
, por exemplo, passando-a para function frobnicateTwice(IApi $api)
. Agora, nessa função, frobnicate
é chamado e, como ele lida apenas com IApi
, ele pode executar uma chamada como $api->frobnicate(new SpecificConfig(...))
onde SpecificConfig
implementa, IConfig
mas não IHttpConfig
. Em nenhum momento alguém fez algo desagradável com os tipos, mas IHttpApi::frobnicate
chegou SpecificConfig
aonde esperava a IHttpConfig
.
Isso não é bom. Não queremos proibir upcasting, queremos subtipagem e queremos claramente várias classes implementando uma interface. Portanto, a única opção sensata é proibir um método de subtipo que exija tipos mais específicos de parâmetros. (Um problema semelhante ocorre quando você deseja retornar um tipo mais geral .)
Formalmente, você entrou em uma armadilha clássica em torno do polimorfismo, variação . Nem todas as ocorrências de um tipo T
podem ser substituídas por um subtipo U
. Por outro lado, nem todas as ocorrências de um tipo T
podem ser substituídas por um supertipo S
. Considerações cuidadosas (ou melhor ainda, aplicação estrita da teoria dos tipos) são necessárias.
Voltando a __construct
: Como o AFAIK você não pode instanciar exatamente uma interface, apenas um implementador concreto, isso pode parecer uma restrição inútil (nunca será chamado por meio de uma interface). Mas, nesse caso, por que incluir __construct
na interface, para começar? Independentemente disso, seria de pouca utilidade para casos especiais __construct
aqui.
Driver
pode conduzir qualquer umVehicle
e, como ambosCar
eMotorcycle
se estendeVehicle
, todos osDriver
s devem ser capazes de lidar com ambos.