PHP interface implementation rejects subclasses on parameters
Asked Answered
E

2

13

consider this:

class A{}

class B extends A{}

interface I{
 // expects object instanceof A
 function doSomething(A $a);
}

class C implements I
{
 // fails ????
 function doSomething(B $b){}
}

In my conception the above should work but it doesn't as php rejects that implementation requiring the first parameter to be exactly the same type (A) as defined in the interface (I). Since B is a subclass of A, I don't see whats the problem. Am I missing something here?

Entomologize answered 24/1, 2011 at 13:59 Comment(9)
Well B is descendent from, but not equal to, A, and PHP's view on this is strict. It's the way it is, I don't think it's possible to work around it - you will probably just have to work without the type hintLebensraum
It won't work. See example 2. php.net/manual/en/language.oop5.interfaces.phpShahjahanpur
Liskov Substitution Principle: if B extends A, there's no reason C::doSometing() should only accept B, and not all objects of A type.Coin
@Coin I agree that logically you would think it should work, but unfortunately it just doesn't in PHP.Shahjahanpur
@Wiseguy: My point is PHP is absolutely right in rejecting this definition.Coin
@Coin Ah, well, ultimately the problem here is the definition mismatch and not subtype substitution. If class C defined function doSomething(A $a){} instead, it should accept an argument of type B.Shahjahanpur
Yes it should. Since B extends A, one should be able to use B in each place where A can be used, so there's no point in limiting the type hint.Coin
By restricting the scope you're breaking the contract of interface I. If the interface says doSomething can be called with A, and your implementation can not called with A (any A, not only a subset of As) then your code certainly does not implement the required behavior.Langille
possible duplicate of Is there a way to redefine a type hint to a descendant class when extending an abstract class?Mackenie
R
12

class C implements I means that there must be subtype relation between C and I. It means object of type C should be usable wherever an object of type I is required.

In your case C is more restrictive than I because it has more precise requirements on its doSomething argument -- I.doSomething is fine with any A but C.doSomething requires a specific subtype of A

Note that if you change C.doSomething to accept any A then nothing prevents you to pass it an object of type B. You just can't require only B, because then you would break subtyping contract.

In theory, subtypes can be more liberal about their function arguments and more specific about their return types (but never vice versa, as it was in your case). In practice, a programming language may require that argument types in overridden methods must be same everywhere.

Rappel answered 24/1, 2011 at 14:11 Comment(3)
The second paragraph of your answer explains everything I needed to know. Thanks.Entomologize
How is C more restrictive than I, if B implements all of A's functionality and then some?Mackenie
@Mackenie -- if B implements all of A's functionality and then some, it's more powerful than A, hence C's doSomething requires more powerful argument than I's doSomething (which is content with a "mere" A. In other words, C's doSomething is more picky than I's doSomething.Rappel
E
4

In theory, subtypes can be more liberal about their function arguments and more specific about their return types (but never vice versa, as it was in your case). In practice, a programming language may require that argument types in overridden methods must be same everywhere.

little work around- instanceof to solve that problem:

class A{}

class B extends A{}

interface I{
 // expects object instanceof A
 function doSomething(A $a);
}

class C implements I
{
 
 function doSomething(A $b){
   if($b instanceof of B){
   //do something
   }else{throw new InvalidArgumentException("arg must be instance of B") };
 }
}
Eximious answered 2/11, 2013 at 12:20 Comment(1)
should "is instance of" not be "instanceof" ?Unprintable

© 2022 - 2024 — McMap. All rights reserved.