有没有关于PHP缺少泛型的体面工作,这些泛型允许静态代码检查以检测类型一致性?
我有一个抽象类,我想对其进行子类化,并且还强制其中一种方法从采用一种类型的参数更改为采用作为该参数的子类的参数。
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
PHP不允许这样做,因为它正在更改不允许的方法签名。使用Java样式泛型,您可以执行以下操作:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
但是很明显,PHP不支持这些功能。
谷歌针对这个问题,人们建议使用instanceof
在运行时检查错误,例如
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item){
if (!($item instanceof WoodItem)) {
throw new \InvalidArgumentException(
"item of class ".get_class($item)." is not a WoodItem");
}
}
}
但这仅在运行时有效,它不允许您使用静态分析检查代码中的错误-因此,在PHP中是否有任何明智的处理方式?
该问题的一个更完整的示例是:
class StoneItem extends Item{}
class WoodItem extends Item{}
class WoodProcessedItem extends ProcessedItem {
function __construct(WoodItem $woodItem){}
}
class StoneProcessedItem extends ProcessedItem{
function __construct(StoneItem $stoneItem){}
}
abstract class AbstractProcessor {
abstract function processItem(Item $item);
function processAndBoxItem(Box $box, Item $item) {
$processedItem = $this->processItem($item);
$box->insertItem($item);
}
//Lots of other functions that can call processItem
}
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedWoodItem($item); //This has an inspection error
}
}
class StoneProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedStoneItem($item);//This has an inspection error
}
}
因为我只传递了一个Item
to new ProcessedWoodItem($item)
并且它期望将WoodItem作为参数,所以代码检查表明存在错误。
1
为什么不使用接口?我相信,您可以使用接口继承来实现所需的功能。
—
RibaldEddie 2014年
因为这两个类共享其代码的80%,所以它们在抽象类中。使用接口意味着要么复制代码,要么进行大量重构以将共享的代码移到可以与这两个类组合在一起的另一个类中。
—
Danack 2014年
我很确定您可以同时使用两者。
—
RibaldEddie 2014年
是的-但是它不能解决以下问题:i)共享方法需要使用“ Item”的基类; ii)特定于类型的方法希望使用子类“ WoodItem”,但是会出现类似“声明”的错误。 Process :: bar()必须与AbstractProcessor :: bar(Item $ item)兼容”
—
Danack 2014年
我没有时间实际测试行为,但是如果您提示接口(创建IItem和IWoodItem并让IWoodItem从IItem继承),该怎么办?然后在基类的函数签名中提示IItem,在子级中提示IWoodItem。达可能会工作。也许不会。
—
RibaldEddie 2014年