1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-24 07:02:08 +01:00
Files
archived-doc-ja/language/oop5/variance.xml
Louis-Arnaud e94b0c04ce 引数(arguments)とパラメータ(parameters)の区別を修正 (#327)
* 引数(arguments)とパラメータ(parameters)の区別を修正

Closes #62

* 引数/パラメータの使い分けを修正(Copilot review反映)

* Use パラメータ consistently per README_Glossary.md
2026-02-27 12:40:26 +09:00

326 lines
8.8 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 2e7c00fd314a708ecbd495ef7cc9ae8c8462c33c Maintainer: mumumu Status: ready -->
<sect1 xml:id="language.oop5.variance" xmlns="http://docbook.org/ns/docbook">
<title>共変性と反変性</title>
<para>
PHP 7.2.0 で、子クラスのメソッドのパラメータの型の制限を除く形で、反変性が一部サポートされました。
PHP 7.4.0 以降で、共変性と反変性が完全にサポートされるようになりました。
</para>
<para>
共変性とは、子クラスのメソッドが、親クラスの戻り値よりも、より特定の、狭い型を返すことを許すことです。
反変性とは、親クラスのものよりも、より抽象的な、広い型をパラメータに指定することを許すものです。
</para>
<para>
型宣言は以下の場合に、より特定の、狭い型であると見なされます:
<itemizedlist>
<listitem>
<simpara>
<link linkend="language.types.type-system.composite.union">union 型</link> から、特定の型が削除されている場合
</simpara>
</listitem>
<listitem>
<simpara>
特定の型が
<link linkend="language.types.type-system.composite.intersection">交差型</link> に追加されている場合
</simpara>
</listitem>
<listitem>
<simpara>
クラスの型が、子クラスの型に変更されている場合
</simpara>
</listitem>
<listitem>
<simpara>
<type>iterable</type> が 配列 または <classname>Traversable</classname> に変更されている場合
</simpara>
</listitem>
</itemizedlist>
上記と反対のことが当てはまる場合は、より抽象的な、広い型であると見なされます。
</para>
<sect2 xml:id="language.oop5.variance.covariance">
<title>共変性</title>
<para>
共変性がどのように動作するかを示すために、
単純な抽象クラスの親である<varname>Animal</varname> を作ることにします。
このクラスは子クラス <varname>Cat</varname><varname>Dog</varname>
に継承されています。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
abstract public function speak();
}
class Dog extends Animal
{
public function speak()
{
echo $this->name . " barks";
}
}
class Cat extends Animal
{
public function speak()
{
echo $this->name . " meows";
}
}
]]>
</programlisting>
</informalexample>
<para>
この例では、どのメソッドも値を返さないことに注意して下さい。
以下ではこれらのクラスを使い、
<varname>Animal</varname>, <varname>Cat</varname> または
<varname>Dog</varname>
クラスの新しいオブジェクトを返すファクトリをいくつか作ってみることにします。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
interface AnimalShelter
{
public function adopt(string $name): Animal;
}
class CatShelter implements AnimalShelter
{
public function adopt(string $name): Cat // Animal 型を返す代わりに、Cat型を返すことができる
{
return new Cat($name);
}
}
class DogShelter implements AnimalShelter
{
public function adopt(string $name): Dog // Animal 型を返す代わりに、Dog型を返すことができる
{
return new Dog($name);
}
}
$kitty = (new CatShelter)->adopt("Ricky");
$kitty->speak();
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$doggy->speak();
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Ricky meows
Mavrick barks
]]>
</screen>
</informalexample>
</sect2>
<sect2 xml:id="language.oop5.variance.contravariance">
<title>反変性</title>
<para>
既に示した <varname>Animal</varname>, <varname>Cat</varname> および
<varname>Dog</varname> クラスの例を引き続き使い、
<varname>Food</varname><varname>AnimalFood</varname> クラスを追加し、
<varname>Animal</varname> 抽象クラスに
<varname>eat(AnimalFood $food)</varname> メソッドを追加してみましょう。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
class Food {}
class AnimalFood extends Food {}
abstract class Animal
{
protected string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function eat(AnimalFood $food)
{
echo $this->name . " eats " . get_class($food);
}
}
]]>
</programlisting>
</informalexample>
<para>
反変性 の振る舞いを見るため、<varname>Dog</varname> クラスの
<varname>eat</varname> メソッドをオーバーライドし、あらゆる
<varname>Food</varname> 型のオブジェクトを受け入れることにします。
<varname>Cat</varname> クラスは変更していません。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
class Dog extends Animal
{
public function eat(Food $food) {
echo $this->name . " eats " . get_class($food);
}
}
]]>
</programlisting>
</informalexample>
<para>
さて、反変性がどのように動くかが以下でわかるでしょう。
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$kitty = (new CatShelter)->adopt("Ricky");
$catFood = new AnimalFood();
$kitty->eat($catFood);
echo "\n";
$doggy = (new DogShelter)->adopt("Mavrick");
$banana = new Food();
$doggy->eat($banana);
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Ricky eats AnimalFood
Mavrick eats Food
]]>
</screen>
<para>
しかし、<varname>$kitty</varname><methodname>eat</methodname> メソッドに
<varname>$banana</varname> を渡すとどうなるでしょう?
</para>
<programlisting role="php">
<![CDATA[
$kitty->eat($banana);
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given
]]>
</screen>
</informalexample>
</sect2>
<sect2>
<title>プロパティの共変性と反変性(変性)</title>
<simpara>
デフォルトでは、プロパティは共変でも反変でもなく不変です。
つまり、子クラスでは型は変更できません。
「get」操作は共変でなければならず、
「set」操作は反変でなければならないことが理由です。
双方を同時に満たすには、プロパティは不変である必要があります。
</simpara>
<simpara>
PHP 8.4.0 から、インターフェイスや抽象クラスでの抽象プロパティや、
<link linkend="language.oop5.property-hooks.virtual">仮想プロパティ</link> が追加されたことにより、
プロパティが「get」または「set」だけを持つことを宣言できるようになりました。
つまり、「get」操作だけが必要な抽象プロパティや仮想プロパティは共変性を持ちます。
同様に、「set」操作だけが必要な抽象プロパティや仮想プロパティは反変性を持ちます。
</simpara>
<simpara>
ただし、いったんプロパティが「get」と「set」操作の両方を持つようになると、
それ以上の拡張において共変あるいは反変にはなりません。
その時点で不変となります。
</simpara>
<example>
<title>プロパティの型の変性</title>
<programlisting role="php">
<![CDATA[
<?php
class Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}
interface PetOwner
{
// 必要なのは「get」操作のみなので、共変です
public Animal $pet { get; }
}
class DogOwner implements PetOwner
{
// 「get」が Animal を返す限り、より狭い型に変更できます。
// しかし、これは通常のプロパティなので、
// 子クラスでは型を変更できません。
public Dog $pet;
}
class PoodleOwner extends DogOwner
{
// これは許可されません。
// DogOwner::$pet は「get」「set」両方の操作を持つためです。
public Poodle $pet;
}
?>
]]>
</programlisting>
</example>
</sect2>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->