<?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 => 'ja',
  ),
  'this' => 
  array (
    0 => 'language.oop5.variance.php',
    1 => '共変性と反変性',
    2 => '共変性と反変性',
  ),
  'up' => 
  array (
    0 => 'language.oop5.php',
    1 => 'クラスとオブジェクト',
  ),
  'prev' => 
  array (
    0 => 'language.oop5.serialization.php',
    1 => 'オブジェクトのシリアライズ',
  ),
  'next' => 
  array (
    0 => 'language.oop5.lazy-objects.php',
    1 => 'レイジーオブジェクト',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ja',
    '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">共変性と反変性</h2>

 <p class="para">
  PHP 7.2.0 で、子クラスのメソッドのパラメータの型の制限を除く形で、反変性が一部サポートされました。
  PHP 7.4.0 以降で、共変性と反変性が完全にサポートされるようになりました。
 </p>

 <p class="para">
  共変性とは、子クラスのメソッドが、親クラスの戻り値よりも、より特定の、狭い型を返すことを許すことです。
  反変性とは、親クラスのものよりも、より抽象的な、広い型をパラメータに指定することを許すものです。
 </p>

 <p class="para">
  型宣言は以下の場合に、より特定の、狭い型であると見なされます:
  <ul class="itemizedlist">
   <li class="listitem">
    <span class="simpara">
     <a href="language.types.type-system.php#language.types.type-system.composite.union" class="link">union 型</a> から、特定の型が削除されている場合
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     特定の型が
     <a href="language.types.type-system.php#language.types.type-system.composite.intersection" class="link">交差型</a> に追加されている場合
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     クラスの型が、子クラスの型に変更されている場合
    </span>
   </li>
   <li class="listitem">
    <span class="simpara">
     <span class="type"><a href="language.types.iterable.php" class="type iterable">iterable</a></span> が 配列 または <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span> に変更されている場合
    </span>
   </li>
  </ul>

  上記と反対のことが当てはまる場合は、より抽象的な、広い型であると見なされます。
 </p>

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

  <p class="para">
   共変性がどのように動作するかを示すために、
   単純な抽象クラスの親である<var class="varname">Animal</var> を作ることにします。
   このクラスは子クラス <var class="varname">Cat</var> と <var class="varname">Dog</var>
   に継承されています。
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="annotation-interactive 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">
   この例では、どのメソッドも値を返さないことに注意して下さい。
   以下ではこれらのクラスを使い、
   <var class="varname">Animal</var>, <var class="varname">Cat</var> または
   <var class="varname">Dog</var>
   クラスの新しいオブジェクトを返すファクトリをいくつか作ってみることにします。
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="annotation-interactive 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">// Animal 型を返す代わりに、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">// Animal 型を返す代わりに、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">上の例の出力は以下となります。</p>
   <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Ricky meows
Mavrick barks
</pre></div>
   </div>
  </div>
 </div>

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

  <p class="para">
   既に示した <var class="varname">Animal</var>, <var class="varname">Cat</var> および
   <var class="varname">Dog</var> クラスの例を引き続き使い、
   <var class="varname">Food</var> と <var class="varname">AnimalFood</var> クラスを追加し、
   <var class="varname">Animal</var> 抽象クラスに
   <var class="varname">eat(AnimalFood $food)</var> メソッドを追加してみましょう。
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="annotation-interactive 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">
   反変性 の振る舞いを見るため、<var class="varname">Dog</var> クラスの
   <var class="varname">eat</var> メソッドをオーバーライドし、あらゆる
   <var class="varname">Food</var> 型のオブジェクトを受け入れることにします。
   <var class="varname">Cat</var> クラスは変更していません。
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="annotation-interactive 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">
   さて、反変性がどのように動くかが以下でわかるでしょう。
  </p>

  <div class="informalexample">
   <div class="example-contents">
<div class="annotation-interactive 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">上の例の出力は以下となります。</p>
   <div class="example-contents screen">
<div class="annotation-interactive cdata"><pre>
Ricky eats AnimalFood
Mavrick eats Food
</pre></div>
   </div>

   <p class="para">
    しかし、<var class="varname">$kitty</var> の <span class="methodname"><strong>eat()</strong></span> メソッドに
    <var class="varname">$banana</var> を渡すとどうなるでしょう？
   </p>

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

   <p class="para">上の例の出力は以下となります。</p>
   <div class="example-contents screen">
<div class="annotation-interactive 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">プロパティの共変性と反変性(変性)</h3>
  <p class="simpara">
   デフォルトでは、プロパティは共変でも反変でもなく不変です。
   つまり、子クラスでは型は変更できません。
   「get」操作は共変でなければならず、
   「set」操作は反変でなければならないことが理由です。
   双方を同時に満たすには、プロパティは不変である必要があります。
  </p>
  <p class="simpara">
   PHP 8.4.0 から、インターフェイスや抽象クラスでの抽象プロパティや、
   <a href="language.oop5.property-hooks.php#language.oop5.property-hooks.virtual" class="link">仮想プロパティ</a> が追加されたことにより、
   プロパティが「get」または「set」だけを持つことを宣言できるようになりました。
   つまり、「get」操作だけが必要な抽象プロパティや仮想プロパティは共変性を持ちます。
   同様に、「set」操作だけが必要な抽象プロパティや仮想プロパティは反変性を持ちます。
  </p>
  <p class="simpara">
   ただし、いったんプロパティが「get」と「set」操作の両方を持つようになると、
   それ以上の拡張において共変あるいは反変にはなりません。
   その時点で不変となります。
  </p>
  <div class="example" id="example-1">
   <p><strong>例1 プロパティの型の変性</strong></p>
   <div class="example-contents">
<div class="annotation-interactive 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">// 必要なのは「get」操作のみなので、共変です<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">// 「get」が Animal を返す限り、より狭い型に変更できます。<br />    // しかし、これは通常のプロパティなので、<br />    // 子クラスでは型を変更できません。<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">// これは許可されません。<br />    // DogOwner::$pet は「get」「set」両方の操作を持つためです。<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); ?>