<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/language.oop5.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'es',
  ),
  'this' => 
  array (
    0 => 'language.oop5.variance.php',
    1 => 'Covarianza y Contravarianza',
    2 => 'Covarianza y Contravarianza',
  ),
  'up' => 
  array (
    0 => 'language.oop5.php',
    1 => 'Clases y objetos',
  ),
  'prev' => 
  array (
    0 => 'language.oop5.serialization.php',
    1 => 'Serializaci&oacute;n de objetos',
  ),
  'next' => 
  array (
    0 => 'language.oop5.lazy-objects.php',
    1 => 'Objetos perezosos',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'es',
    'path' => 'language/oop5/variance.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="language.oop5.variance" class="sect1">
 <h2 class="title">Covarianza y Contravarianza</h2>

 <p class="para">
  A partir de PHP 7.2.0, se introdujo la contravarianza parcial eliminando las restricciones de tipo
  en los parámetros de un método hijo. A partir de PHP 7.4.0, se añadieron la covarianza y la contravarianza completas.
 </p>
 <p class="para">
  La covarianza permite a un método hijo devolver un tipo más específico que el tipo de retorno
  de su método padre. La contravarianza permite que un tipo de parámetro sea menos específico
  en un método hijo que en el de la clase padre.
 </p>
 <p class="para">
  Una declaración de tipo se considera más específica en el siguiente caso:
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     Un tipo es retirado de un
    <a href="language.types.type-system.php#language.types.type-system.composite.union" class="link">tipo union</a>
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Un tipo es añadido a un
    <a href="language.types.type-system.php#language.types.type-system.composite.intersection" class="link">tipo de intersección</a>
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     Un tipo de clase es transformado en un tipo de clase hijo
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
    <span class="type"><a href="language.types.iterable.php" class="type iterable">iterable</a></span> es reemplazado por <span class="type"><a href="language.types.array.php" class="type array">array</a></span> o <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span>
    </span>
   </li>
  </ul>

  Un tipo de clase se considera menos específico si lo contrario es cierto.
 </p>

 <div class="sect2" id="language.oop5.variance.covariance">
  <h3 class="title">Covarianza</h3>

  <p class="para">
   Para ilustrar el funcionamiento de la covarianza, se crea una simple clase padre abstracta, <var class="varname">Animal</var>.
   <var class="varname">Animal</var> será extendida por clases hijas,
  <var class="varname">Cat</var> y <var class="varname">Dog</var>.
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">abstract class </span><span style="color: #0000BB">Animal<br /></span><span style="color: #007700">{<br />    protected </span><span style="color: #0000BB">string $name</span><span style="color: #007700">;<br /><br />    public function </span><span style="color: #0000BB">__construct</span><span style="color: #007700">(</span><span style="color: #0000BB">string $name</span><span style="color: #007700">)<br />    {<br />        </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">= </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br />    }<br /><br />    abstract public function </span><span style="color: #0000BB">speak</span><span style="color: #007700">();<br />}<br /><br />class </span><span style="color: #0000BB">Dog </span><span style="color: #007700">extends </span><span style="color: #0000BB">Animal<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">speak</span><span style="color: #007700">()<br />    {<br />        echo </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">. </span><span style="color: #DD0000">" barks"</span><span style="color: #007700">;<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">Cat </span><span style="color: #007700">extends </span><span style="color: #0000BB">Animal<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">speak</span><span style="color: #007700">()<br />    {<br />        echo </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">. </span><span style="color: #DD0000">" meows"</span><span style="color: #007700">;<br />    }<br />}</span></span></code></div>
   </div>

  </div>

  <p class="para">
   Téngase en cuenta que no hay métodos que devuelvan valores en este ejemplo. Se añadirán algunas fábricas
   y devolverán un nuevo objeto de clase de tipo <var class="varname">Animal</var>,
  <var class="varname">Cat</var>, o <var class="varname">Dog</var>.
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">interface </span><span style="color: #0000BB">AnimalShelter<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #0000BB">string $name</span><span style="color: #007700">): </span><span style="color: #0000BB">Animal</span><span style="color: #007700">;<br />}<br /><br />class </span><span style="color: #0000BB">CatShelter </span><span style="color: #007700">implements </span><span style="color: #0000BB">AnimalShelter<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #0000BB">string $name</span><span style="color: #007700">): </span><span style="color: #0000BB">Cat </span><span style="color: #FF8000">// en lugar de devolver el tipo de clase Animal, puede devolver el tipo de clase Cat<br />    </span><span style="color: #007700">{<br />        return new </span><span style="color: #0000BB">Cat</span><span style="color: #007700">(</span><span style="color: #0000BB">$name</span><span style="color: #007700">);<br />    }<br />}<br /><br />class </span><span style="color: #0000BB">DogShelter </span><span style="color: #007700">implements </span><span style="color: #0000BB">AnimalShelter<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #0000BB">string $name</span><span style="color: #007700">): </span><span style="color: #0000BB">Dog </span><span style="color: #FF8000">// en lugar de devolver el tipo de clase Animal, puede devolver el tipo de clase Dog<br />    </span><span style="color: #007700">{<br />        return new </span><span style="color: #0000BB">Dog</span><span style="color: #007700">(</span><span style="color: #0000BB">$name</span><span style="color: #007700">);<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$kitty </span><span style="color: #007700">= (new </span><span style="color: #0000BB">CatShelter</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #DD0000">"Ricky"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$kitty</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">speak</span><span style="color: #007700">();<br />echo </span><span style="color: #DD0000">"\n"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$doggy </span><span style="color: #007700">= (new </span><span style="color: #0000BB">DogShelter</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #DD0000">"Mavrick"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$doggy</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">speak</span><span style="color: #007700">();</span></span></code></div>
   </div>

   <p class="para">El ejemplo anterior mostrará:</p>
   <div class="example-contents screen">
<div class="cdata"><pre>
Ricky meows
Mavrick barks
</pre></div>
   </div>
  </div>
 </div>

 <div class="sect2" id="language.oop5.variance.contravariance">
  <h3 class="title">Contravarianza</h3>

  <p class="para">
   Retomando el ejemplo anterior con las clases <var class="varname">Animal</var>,
   <var class="varname">Cat</var> y <var class="varname">Dog</var>, se incluyen dos clases llamadas
   <var class="varname">Food</var> y <var class="varname">AnimalFood</var>, y
   se añade un método <var class="varname">eat(AnimalFood $food)</var> a la clase abstracta
   <var class="varname">Animal</var>.
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Food </span><span style="color: #007700">{}<br /><br />class </span><span style="color: #0000BB">AnimalFood </span><span style="color: #007700">extends </span><span style="color: #0000BB">Food </span><span style="color: #007700">{}<br /><br />abstract class </span><span style="color: #0000BB">Animal<br /></span><span style="color: #007700">{<br />    protected </span><span style="color: #0000BB">string $name</span><span style="color: #007700">;<br /><br />    public function </span><span style="color: #0000BB">__construct</span><span style="color: #007700">(</span><span style="color: #0000BB">string $name</span><span style="color: #007700">)<br />    {<br />        </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">= </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br />    }<br /><br />    public function </span><span style="color: #0000BB">eat</span><span style="color: #007700">(</span><span style="color: #0000BB">AnimalFood $food</span><span style="color: #007700">)<br />    {<br />        echo </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">. </span><span style="color: #DD0000">" eats " </span><span style="color: #007700">. </span><span style="color: #0000BB">get_class</span><span style="color: #007700">(</span><span style="color: #0000BB">$food</span><span style="color: #007700">);<br />    }<br />}</span></span></code></div>
   </div>

  </div>

  <p class="para">
   Para ver el comportamiento de la contravarianza, el método
   método <var class="varname">eat</var> es sobrecargado en la clase <var class="varname">Dog</var> para permitir
   cualquier objeto de tipo <var class="varname">Food</var>. La clase <var class="varname">Cat</var> permanece sin cambios.
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Dog </span><span style="color: #007700">extends </span><span style="color: #0000BB">Animal<br /></span><span style="color: #007700">{<br />    public function </span><span style="color: #0000BB">eat</span><span style="color: #007700">(</span><span style="color: #0000BB">Food $food</span><span style="color: #007700">) {<br />        echo </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name </span><span style="color: #007700">. </span><span style="color: #DD0000">" eats " </span><span style="color: #007700">. </span><span style="color: #0000BB">get_class</span><span style="color: #007700">(</span><span style="color: #0000BB">$food</span><span style="color: #007700">);<br />    }<br />}</span></span></code></div>
   </div>

  </div>

  <p class="para">
   El siguiente ejemplo muestra el comportamiento de la contravarianza.
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$kitty </span><span style="color: #007700">= (new </span><span style="color: #0000BB">CatShelter</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #DD0000">"Ricky"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$catFood </span><span style="color: #007700">= new </span><span style="color: #0000BB">AnimalFood</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$kitty</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">eat</span><span style="color: #007700">(</span><span style="color: #0000BB">$catFood</span><span style="color: #007700">);<br />echo </span><span style="color: #DD0000">"\n"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$doggy </span><span style="color: #007700">= (new </span><span style="color: #0000BB">DogShelter</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">adopt</span><span style="color: #007700">(</span><span style="color: #DD0000">"Mavrick"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$banana </span><span style="color: #007700">= new </span><span style="color: #0000BB">Food</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$doggy</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">eat</span><span style="color: #007700">(</span><span style="color: #0000BB">$banana</span><span style="color: #007700">);</span></span></code></div>
   </div>

   <p class="para">El ejemplo anterior mostrará:</p>
   <div class="example-contents screen">
<div class="cdata"><pre>
Ricky eats AnimalFood
Mavrick eats Food
</pre></div>
   </div>

  <p class="para">
   Pero, ¿qué sucede si <var class="varname">$kitty</var> intenta comer (<span class="methodname"><strong>eat()</strong></span>) la
   banana (<var class="varname">$banana</var>) ?
  </p>

   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">$kitty-&gt;eat($banana);</span></code></div>
   </div>

   <p class="para">El ejemplo anterior mostrará:</p>
   <div class="example-contents screen">
<div class="cdata"><pre>
Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given
</pre></div>
   </div>
  </div>
 </div>
 <div class="sect2">
  <h3 class="title">Variación de tipo de las propiedades</h3>
  <p class="simpara">
   Por defecto, las propiedades no son ni covariantes ni contravariantes, por lo tanto, son invariantes.
   En otras palabras, su tipo no puede cambiar en absoluto en una clase hija.
   La razón es que las operaciones &quot;get&quot; deben ser covariantes,
   y las operaciones &quot;set&quot; deben ser contravariantes.
   La única manera para que una propiedad cumpla con estos dos requisitos es ser invariante.
  </p>
  <p class="simpara">
   A partir de PHP 8.4.0, con la adición de las propiedades abstractas (en una interfaz o una clase abstracta) y
   <a href="language.oop5.property-hooks.php#language.oop5.property-hooks.virtual" class="link">propiedades virtuales</a>,
   es posible declarar una propiedad que solo tenga una operación &quot;get&quot; o &quot;set&quot;.
   En consecuencia, las propiedades abstractas o las propiedades virtuales que solo requieren la operación &quot;get&quot; pueden ser covariantes.
   De manera similar, una propiedad abstracta o una propiedad virtual que solo requiere la operación &quot;set&quot; puede ser contravariante.
  </p>
  <p class="simpara">
   Sin embargo, una vez que una propiedad tiene tanto una operación &quot;get&quot; como &quot;set&quot;,
   ya no es covariante ni contravariante para una extensión futura.
   En otras palabras, se vuelve invariante.
  </p>
  <div class="example" id="example-1">
   <p><strong>Ejemplo #1 Variación del tipo de las propiedades</strong></p>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Animal </span><span style="color: #007700">{}<br />class </span><span style="color: #0000BB">Dog </span><span style="color: #007700">extends </span><span style="color: #0000BB">Animal </span><span style="color: #007700">{}<br />class </span><span style="color: #0000BB">Poodle </span><span style="color: #007700">extends </span><span style="color: #0000BB">Dog </span><span style="color: #007700">{}<br /><br />interface </span><span style="color: #0000BB">PetOwner<br /></span><span style="color: #007700">{<br />    </span><span style="color: #FF8000">// Solo se requiere la operación "get", por lo tanto, puede ser covariante.<br />    </span><span style="color: #007700">public </span><span style="color: #0000BB">Animal $pet </span><span style="color: #007700">{ </span><span style="color: #0000BB">get</span><span style="color: #007700">; }<br />}<br /><br />class </span><span style="color: #0000BB">DogOwner </span><span style="color: #007700">implements </span><span style="color: #0000BB">PetOwner<br /></span><span style="color: #007700">{<br />    </span><span style="color: #FF8000">// Puede ser un tipo más restrictivo, ya que el lado "get"<br />    // siempre devuelve un Animal. Sin embargo, como propiedad nativa,<br />    // los hijos de esta clase ya no pueden cambiar el tipo.<br />    </span><span style="color: #007700">public </span><span style="color: #0000BB">Dog $pet</span><span style="color: #007700">;<br />}<br /><br />class </span><span style="color: #0000BB">PoodleOwner </span><span style="color: #007700">extends </span><span style="color: #0000BB">DogOwner<br /></span><span style="color: #007700">{<br />    </span><span style="color: #FF8000">// ESTO NO ESTÁ PERMITIDO, ya que DogOwner::$pet tiene tanto<br />    // las operaciones "get" como "set" definidas y requeridas.<br />    </span><span style="color: #007700">public </span><span style="color: #0000BB">Poodle $pet</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </div>
</div><?php manual_footer($setup); ?>