Magento2 – Can’t Inject Dependency in Class Extending ListProduct

dependency-injectionmagento2

I'm trying to extend a Block in Magento, the Magento\Catalog\Block\Product\ListProduct block and Inject a dependency on it, but I've hit a wall and just get this error.

Fatal error: Uncaught TypeError: Argument 2 passed to Holy\Demo\Block\Product\ListProduct::__construct() must implement interface Magento\CatalogInventory\Api\StockStateInterface, instance of Magento\Framework\Data\Helper\PostHelper given, called in /var/www/html/generated/code/Holy/Demo/Block/Product/ListProduct/Interceptor.php on line 14 and defined in /var/www/html/app/code/Holy/Demo/Block/Product/ListProduct.php:14 Stack trace: #0 /var/www/html/generated/code/Holy/Demo/Block/Product/ListProduct/Interceptor.php(14): Holy\Demo\Block\Product\ListProduct->__construct(Object(Magento\Catalog\Block\Product\Context), Object(Magento\Framework\Data\Helper\PostHelper), Object(Magento\Catalog\Model\Layer\Resolver), Object(Magento\Catalog\Model\CategoryRepository\Interceptor), Object(Magento\Framework\Url\Helper\Data), Array) #1 /var/www/html/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php(111): Holy\Demo\Block\Product\ListProduct\Inte in /var/www/html/app/code/Holy/Demo/Block/Product/ListProduct.php on line 14

This is the di.xml of my module:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Block\Product\ListProduct" type="Holy\Demo\Block\Product\ListProduct" />
</config>

And this is my Block class:

<?php

namespace Holy\Demo\Block\Product;

/**
 * Product list
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class ListProduct extends \Magento\Catalog\Block\Product\ListProduct
{
    /**
     * @param \Magento\CatalogInventory\Api\StockStateInterface $stockState
     */
   public function __construct(
       \Magento\Catalog\Block\Product\Context $context,
//        \Magento\Backend\Block\Template\Context $context,
       \Magento\CatalogInventory\Api\StockStateInterface $stockState,
       array $data = []
   )
   {
       $this->stockState = $stockState;
       parent::__construct($context, $data);
   }

    public function getStockQty($product) {
      // TODO: Don't use objectManager, Inject the \Magento\CatalogInventory\Api\StockStateInterface on the constructor of the block
      // $_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
      // $_stockState = $_objectManager->get('\Magento\CatalogInventory\Api\StockStateInterface');
      // return $_stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId());

       return $this->stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId());
    }
}

UPDATE

I was able to get it working by adding all of the parent class arguments from it's constructor to my constructor, but this does not seem like a good approach to me as if in a Magento update a new dependency is added to the parent class, won't that break my module?

public function __construct(
    \Magento\Catalog\Block\Product\Context $context,
    \Magento\Framework\Data\Helper\PostHelper $postDataHelper,
    \Magento\Catalog\Model\Layer\Resolver $layerResolver,
    \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
    \Magento\Framework\Url\Helper\Data $urlHelper,
    \Magento\CatalogInventory\Api\StockStateInterface $stockState,
    array $data = []
)
{
    $this->stockState = $stockState;
    parent::__construct($context, $postDataHelper , $layerResolver, $categoryRepository, $urlHelper, $data);
}

Best Answer

Your child arguments constructor should inherit from parent class:

 public function __construct(
        \Magento\Catalog\Block\Product\Context $context, 
        \Magento\Framework\Data\Helper\PostHelper $postDataHelper, 
        \Magento\Catalog\Model\Layer\Resolver $layerResolver,
        \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository, 
        \Magento\Framework\Url\Helper\Data $urlHelper, 
        array $data = []
    )
    {
        parent::__construct($context, $postDataHelper, $layerResolver, $categoryRepository, $urlHelper, $data);
    }

Remember to remove var/generation and clear cache also.

[EDIT]

For this answer: Magento 2 StoreManagerInterface already exists in context object in compilation. We don't need to inject \Magento\Store\Model\StoreManagerInterface in our child class, but we inject \Magento\Framework\View\Element\Context in the construct. This class has \Magento\Store\Model\StoreManagerInterface in its constructor already.