-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathvariance.xml
325 lines (285 loc) · 8.78 KB
/
variance.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
<?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
-->