Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP对Java风格的类泛型有答案吗?_Java_Php_Oop_Generics_Inheritance - Fatal编程技术网

PHP对Java风格的类泛型有答案吗?

PHP对Java风格的类泛型有答案吗?,java,php,oop,generics,inheritance,Java,Php,Oop,Generics,Inheritance,在用PHP构建MVC框架时,我遇到了一个问题,这个问题可以使用Java风格的泛型轻松解决。抽象控制器类可能如下所示: abstract class Controller { abstract public function addModel(Model $model); 可能存在这样一种情况:类控制器的子类应该只接受模型的子类。例如,ExtendedController应该只接受addModel方法中的ReOrderableModel,因为它提供了ExtendedController需要访问

在用PHP构建MVC框架时,我遇到了一个问题,这个问题可以使用Java风格的泛型轻松解决。抽象控制器类可能如下所示:

abstract class Controller {

abstract public function addModel(Model $model);
可能存在这样一种情况:类控制器的子类应该只接受模型的子类。例如,ExtendedController应该只接受addModel方法中的ReOrderableModel,因为它提供了ExtendedController需要访问的reOrder()方法:

class ExtendedController extends Controller {

public function addModel(ReOrderableModel $model) {
在PHP中,继承的方法签名必须完全相同,因此不能将类型提示更改为其他类,即使该类继承了超类中暗示的类类型。在java中,我只需执行以下操作:

abstract class Controller<T> {

abstract public addModel(T model);


class ExtendedController extends Controller<ReOrderableModel> {

public addModel(ReOrderableModel model) {
抽象类控制器{
抽象公共模型(T模型);
类ExtendedController扩展控制器{
公共addModel(重新降额模型){
但是PHP中没有泛型支持。有没有仍然遵循OOP原则的解决方案

编辑 我知道PHP根本不需要类型暗示,但它可能是糟糕的OOP应该接受哪种类型的对象。因此,如果另一个开发人员想要使用该方法,那么很明显,X类型的对象是必需的,而不必查看实现(方法体)这是糟糕的封装,破坏了信息隐藏原则。其次,由于没有类型安全性,该方法可以接受任何无效变量,这意味着需要到处进行手动类型检查和异常抛出!

在以下测试用例中,它似乎对我有效(尽管它确实发出严格警告):

class PassMeIn
{

}

class PassMeInSubClass extends PassMeIn
{

}

class ClassProcessor
{
    public function processClass (PassMeIn $class)
    {
        var_dump (get_class ($class));
    }
}

class ClassProcessorSubClass extends ClassProcessor 
{
    public function processClass (PassMeInSubClass $class)
    {
        parent::processClass ($class);
    }
}

$a  = new PassMeIn;
$b  = new PassMeInSubClass;
$c  = new ClassProcessor;
$d  = new ClassProcessorSubClass;

$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
如果严格的警告是你真正不想要的,你可以这样处理它

class ClassProcessor
{
    public function processClass (PassMeIn $class)
    {
        var_dump (get_class ($class));
    }
}

class ClassProcessorSubClass extends ClassProcessor 
{
    public function processClass (PassMeIn $class)
    {
        if ($class instanceof PassMeInSubClass)
        {
            parent::processClass ($class);
        }
        else
        {
            throw new InvalidArgumentException;
        }
    }
}

$a  = new PassMeIn;
$b  = new PassMeInSubClass;
$c  = new ClassProcessor;
$d  = new ClassProcessorSubClass;

$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
$d -> processClass ($a);
不过,有一点您应该记住,这在OOP术语中绝对不是最佳实践。如果超类可以接受特定类的对象作为方法参数,那么其所有子类也应该能够接受该类的对象。阻止子类处理超类可以接受的类意味着不能使用子类代替超类,并且100%相信它在所有情况下都能工作。相关实践称为,它指出,除其他外,方法参数的类型只能在子类中变弱,返回值的类型只能变强(输入只能更一般,输出只能更具体)


这是一个非常令人沮丧的问题,我自己也曾多次遇到过,因此如果在特定情况下忽略它是最好的做法,那么我建议您忽略它。但不要养成习惯,否则您的代码将开始发展各种微妙的相互依赖关系,这将是调试的噩梦(单元测试无法捕捉到它们,因为单个单元的行为将如预期的那样,这是它们之间的交互问题所在)。如果您确实忽略了它,请对代码进行注释,让其他人知道它,这是一个深思熟虑的设计选择。

您可以通过将类型作为构造函数的第二个参数进行传递来实现

<?php class Collection implements IteratorAggregate{
      private $type;
      private $container;
      public function __construct(array $collection, $type='Object'){
          $this->type = $type;
          foreach($collection as $value){
             if(!($value instanceof $this->type)){
                 throw new RuntimeException('bad type for your collection');
             }  
          }
          $this->container = new \ArrayObject($collection);
      }
      public function getIterator(){
         return $this->container->getIterator();
      }
    }

我以前也遇到过同样的问题。我用类似的方法来解决它

Class Myclass {

    $objectParent = "MyMainParent"; //Define the interface or abstract class or the main parent class here
    public function method($classObject) {
        if(!$classObject instanceof $this -> objectParent) { //check 
             throw new Exception("Invalid Class Identified");
        }
        // Carry on with the function
    }

}

我的解决方法如下:

/**
 * Generic list logic and an abstract type validator method.
 */    
abstract class AbstractList {
    protected $elements;

    public function __construct() {
        $this->elements = array();
    }

    public function add($element) {
        $this->validateType($element);
        $this->elements[] = $element;
    }

    public function get($index) {
        if ($index >= sizeof($this->elements)) {
            throw new OutOfBoundsException();
        }
        return $this->elements[$index];
    }

    public function size() {
        return sizeof($this->elements);
    }

    public function remove($element) {
        validateType($element);
        for ($i = 0; $i < sizeof($this->elements); $i++) {
            if ($this->elements[$i] == $element) {
               unset($this->elements[$i]);
            }
        }
    }

    protected abstract function validateType($element);
}


/**
 * Extends the abstract list with the type-specific validation
 */
class MyTypeList extends AbstractList {
    protected function validateType($element) {
        if (!($element instanceof MyType)) {
            throw new InvalidArgumentException("Parameter must be MyType instance");
        }
    }
}

/**
 * Just an example class as a subject to validation.
 */
class MyType {
    // blahblahblah
}


function proofOfConcept(AbstractList $lst) {
    $lst->add(new MyType());
    $lst->add("wrong type"); // Should throw IAE
}

proofOfConcept(new MyTypeList());
/**
*泛型列表逻辑和抽象类型验证器方法。
*/    
抽象类抽象列表{
受保护的元素;
公共函数构造(){
$this->elements=array();
}
公共功能添加($element){
$this->validateType($element);
$this->elements[]=$element;
}
公共函数get($index){
如果($index>=sizeof($this->elements)){
抛出新的边界外sexception();
}
返回$this->elements[$index];
}
公共功能大小(){
返回sizeof($this->elements);
}
公共功能删除($element){
validateType($element);
对于($i=0;$ielements);$i++){
如果($this->elements[$i]==$element){
未设置($this->elements[$i]);
}
}
}
受保护的抽象函数validateType($element);
}
/**
*使用特定于类型的验证扩展抽象列表
*/
类MyTypeList扩展了AbstractList{
受保护的函数validateType($element){
if(!($element instanceof MyType)){
抛出新的InvalidArgumentException(“参数必须是MyType实例”);
}
}
}
/**
*只是一个示例类作为要验证的主题。
*/
类MyType{
//布拉布拉布拉赫
}
概念的函数证明(AbstractList$lst){
$lst->add(新建MyType());
$lst->add(“错误类型”);//应该抛出
}
概念证明(新MyTypeList());
尽管这仍然不同于Java泛型,但它几乎将模拟行为所需的额外代码减至最少

此外,它的代码比其他人给出的一些示例要多一些,但是-至少对我来说-它似乎比大多数示例更干净(并且更类似于Java对应的示例)

我希望你们中的一些人会觉得它有用


欢迎对该设计进行任何改进!

无论Java世界发明了什么,都不一定总是正确的。我想我在这里发现了对Liskov替换原则的违反,PHP在E_STRICT模式下抱怨这一点是正确的:

引用维基百科:“如果S是T的一个子类型,那么程序中T类型的对象可以替换为S类型的对象,而不会改变该程序的任何理想属性。”

T是您的控制器。S是您的ExtendedController。您应该能够在控制器工作的每个地方使用ExtendedController,而不会破坏任何东西。在addModel()方法上更改typehint会破坏任何东西,因为在每个传递Model类型对象的地方,typeh
class ExtendedController extends Controller
{
  public function addReOrderableModel(ReOrderableModel $model)
  {
    return $this->addModel($model);
  }
}
/**
 * Class GenericCollection
 */
class GenericCollection implements \IteratorAggregate, \ArrayAccess{
    /**
     * @var string
     */
    private $type;

    /**
     * @var array
     */
    private $items = [];

    /**
     * GenericCollection constructor.
     *
     * @param string $type
     */
    public function __construct(string $type){
        $this->type = $type;
    }

    /**
     * @param $item
     *
     * @return bool
     */
    protected function checkType($item): bool{
        $type = $this->getType();
        return $item instanceof $type;
    }

    /**
     * @return string
     */
    public function getType(): string{
        return $this->type;
    }

    /**
     * @param string $type
     *
     * @return bool
     */
    public function isType(string $type): bool{
        return $this->type === $type;
    }

    #region IteratorAggregate

    /**
     * @return \Traversable|$type
     */
    public function getIterator(): \Traversable{
        return new \ArrayIterator($this->items);
    }

    #endregion

    #region ArrayAccess

    /**
     * @param mixed $offset
     *
     * @return bool
     */
    public function offsetExists($offset){
        return isset($this->items[$offset]);
    }

    /**
     * @param mixed $offset
     *
     * @return mixed|null
     */
    public function offsetGet($offset){
        return isset($this->items[$offset]) ? $this->items[$offset] : null;
    }

    /**
     * @param mixed $offset
     * @param mixed $item
     */
    public function offsetSet($offset, $item){
        if(!$this->checkType($item)){
            throw new \InvalidArgumentException('invalid type');
        }
        $offset !== null ? $this->items[$offset] = $item : $this->items[] = $item;
    }

    /**
     * @param mixed $offset
     */
    public function offsetUnset($offset){
        unset($this->items[$offset]);
    }

    #endregion
}


/**
 * Class Item
 */
class Item{
    /**
     * @var int
     */
    public $id = null;

    /**
     * @var string
     */
    public $data = null;

    /**
     * Item constructor.
     *
     * @param int    $id
     * @param string $data
     */
    public function __construct(int $id, string $data){
        $this->id = $id;
        $this->data = $data;
    }
}


/**
 * Class ItemCollection
 */
class ItemCollection extends GenericCollection{
    /**
     * ItemCollection constructor.
     */
    public function __construct(){
        parent::__construct(Item::class);
    }

    /**
     * @return \Traversable|Item[]
     */
    public function getIterator(): \Traversable{
        return parent::getIterator();
    }
}


/**
 * Class ExampleService
 */
class ExampleService{
    /**
     * @var ItemCollection
     */
    private $items = null;

    /**
     * SomeService constructor.
     *
     * @param ItemCollection $items
     */
    public function __construct(ItemCollection $items){
        $this->items = $items;
    }

    /**
     * @return void
     */
    public function list(){
        foreach($this->items as $item){
            echo $item->data;
        }
    }
}


/**
 * Usage
 */
$collection = new ItemCollection;
$collection[] = new Item(1, 'foo');
$collection[] = new Item(2, 'bar');
$collection[] = new Item(3, 'foobar');

$collection[] = 42; // InvalidArgumentException: invalid type

$service = new ExampleService($collection);
$service->list();
class ExampleService{
    public function __construct(Collection<Item> $items){
        // ..
    }
}
<?php

class Student {
    
    public string $name;
    public function __construct(string $name){
        $this->name = $name;
    }
    
}

class Classe {
    
    private $students = [];
    
    public function add(Student ...$student){
        array_merge($this->students, $student);
    }
    
    public function getAll(){
        return $this->students;
    }
    
}

$c = new Classe();
$c->add(new Student('John'), new Student('Mary'), new Student('Kate'));