0

<? 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 的)。

4

0 回答 0