<? super X>
当我们用 Java 写/时,我们应该用 PHP 写什么(由 PHPStan 检查)<? extends X>
?
例子:
<?php declare(strict_types = 1);
// --- Some definitions ---
class Animal {}
class Dog extends Animal { public function bark():void {} }
class Fruit {}
class Apple extends Fruit { public function peel():void {} }
/** @template T */
interface Processor {
/** @param T $value */
public function process($value):void;
}
/**
* @template U
* @template V
*/
interface Transformer {
/**
* @param U $value
* @return V
*/
public function transform($value);
}
// --- Problematic definitions ---
/**
* This is not what we want. We
* would like to specify something
* like `Processor<? super Dog>` so
* that a `Processor<Animal>` would
* also be accepted.
* vvv
* @param Processor<Dog> $processor
*/
function processDog(Dog $dog, Processor $processor):void {
$dog->bark();
$processor->process($dog);
}
/**
* @template U
* @template V
* v------------ bad: want `<? extends U>`
* @param array<U> $source
* vvvv--- bad: want `<? super U, ? extends V>`
* @param Transformer<U, V> $transformer
* v------------ bad: want `<? super V>
* @param array<V> $target
*/
function appendTransformed(array $source, Transformer $transformer, array &$target):void {
foreach ($source as $item) {
$target[] = $transformer->transform($item);
}
}
// --- Bad consequences ---
/**
* @param Dog $dog
* @param Processor<Animal> $processor
*/
function problemIllustration1(Dog $dog, Processor $processor):void {
processDog($dog, $processor); // <--- rejected by PHPStan
}
/**
* @param array<Dog> $source
* @param Transformer<Animal, Apple> $transformer
* @param array<Fruit> $target
*/
function problemIllustration2(array $source, Transformer $transformer, array $target):void {
appendTransformed($source, $transformer, $target); // <--- rejected by PHPStan
}
似乎我们可以<? extends X>
通过引入另一个来获得类似的东西@template
。我们也可以使用 thatarray<Apple>
被视为 an array<Fruit>
(但Transformer<Animal, Apple>
不被视为 an Transformer<Animal, Fruit>
,类似于 Java)。
/**
* @template U
* @template V
* @template V2 of V
* @param array<U> $source
* @param Transformer<U, V2> $transformer
* @param array<V> $target
*/
function appendTransformed(array $source, Transformer $transformer, array &$target):void {
foreach ($source as $item) {
$target[] = $transformer->transform($item);
}
}
我真的不知道如何获得<? super X>
行为。我们尝试将这种方法与其他方法一起使用,@template
并发现了这一点:
/**
* @template S
* @template T of S
* @param T $dog
* @param Processor<S> $processor
*/
function processDog($dog, Processor $processor):void {
$dog->bark(); // <- accepted but should be rejected
$processor->process($dog); // <- rejected but should be accepted
}
我真的很想能够使用<? super X>
类型(较高优先级:PHPStan 接受代码;较低优先级:实际上是类型安全的)。怎么可能做到?
PS:如果您的替换<? super X>
明确地引用了已知的超类型(如Animal
/ Fruit
),请尝试解释在不知道此类类型的情况下如何使用该方法。据我所知,PHP 没有Object
始终可以使用的“通用根/基本类型”(如 Java 的)。