Ignoriamo per un secondo che il metodo in questione è __constructe chiamiamolo frobnicate. Supponiamo ora di avere un oggetto che apiimplementa IHttpApie un oggetto che configimplementa IHttpConfig. Chiaramente, questo codice si adatta all'interfaccia:
$api->frobnicate($config)
Ma andiamo supponiamo upcast apiper IApi, ad esempio, di passarlo a function frobnicateTwice(IApi $api). Ora in quella funzione, frobnicateviene chiamato, e poiché si occupa solo di IApi, può eseguire una chiamata come $api->frobnicate(new SpecificConfig(...))dove SpecificConfigimplementa IConfigma non IHttpConfig. In nessun caso qualcuno ha fatto qualcosa di sgradevole con i tipi, ma ha IHttpApi::frobnicateottenuto un punto SpecificConfigdove si aspettava a IHttpConfig.
Questo non va bene. Non vogliamo vietare l'upgrade, vogliamo il sottotitolo e vogliamo chiaramente che più classi implementino un'interfaccia. Quindi l'unica opzione sensata è proibire un metodo di sottotipo che richiede tipi più specifici per i parametri. (Un problema simile si verifica quando si desidera restituire un tipo più generale .)
Formalmente, sei entrato in una classica trappola che circonda il polimorfismo, la varianza . Non tutte le ricorrenze di un tipo Tpossono essere sostituite da un sottotipo U. Al contrario, non tutte le ricorrenze di un tipo Tpossono essere sostituite da un supertipo S . È necessaria un'attenta considerazione (o meglio, rigorosa applicazione della teoria dei tipi).
Tornando a __construct: Dal momento che AFAIK non è possibile creare un'istanza di un'interfaccia, ma solo un implementatore concreto, questa può sembrare una limitazione inutile (non verrà mai chiamata attraverso un'interfaccia). Ma in tal caso, perché includere __constructnell'interfaccia per cominciare? Indipendentemente da ciò, sarebbe di scarsa utilità in __constructquesto caso speciale .