1
0
mirror of https://github.com/php/doc-ja.git synced 2026-03-27 16:42:17 +01:00
Files
archived-doc-ja/language/oop5/interfaces.xml
2025-08-28 19:57:49 +09:00

483 lines
15 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 565bd8b6cf2cae44ae2bc54ef6dbe6ee70ddfefd Maintainer: hirokawa Status: ready -->
<!-- CREDITS: shimooka,takagi,mumumu -->
<sect1 xml:id="language.oop5.interfaces" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>オブジェクト インターフェイス</title>
<para>
オブジェクト インターフェースでは、クラスが実装すべきメソッドやプロパティの
宣言だけを行うコードを作成できます。ここでは具体的な実装は必要ありません。
インターフェイスはクラス、トレイト、列挙型と名前空間を共有するので、
それらと同じ名前を使ってはいけません。
</para>
<para>
インターフェイスは通常のクラスと同様に定義することができますが、
キーワード <literal>class</literal> のかわりに <literal>interface</literal>
を用います。またメソッドの実装は全く定義しません。
</para>
<para>
インターフェイス内で宣言される全てのメソッドは public である必要があります。
これは、インターフェイスの特性によります。
</para>
<para>
インターフェイスには、ふたつの互いを補完する役割があります。
</para>
<simplelist>
<member>
同じインターフェイスを実装していることで、
開発者が交換可能な異なるクラスを作成できるようにします。
同じインターフェイスを持つクラスによくある例として、
複数のデータベースにアクセスするサービスや
決済のゲートウェイ、
異なるキャッシュ戦略が挙げられます。
実装が異なっていても、
それを使うコードに変更を加えることなく、それらを交換することができます。
</member>
<member>
メソッドや関数が、インターフェイスを満たす引数を受け付け、
操作できるようにします。
オブジェクトが何をするのかや、
どう実装されているのかを気にする必要はありません。
振る舞いの重要性を説明するために、
<literal>Iterable</literal>
<literal>Cacheable</literal>
<literal>Renderable</literal> のような名前が付けられることがよくあります。
</member>
</simplelist>
<para>
インターフェイスは、
<link linkend="language.oop5.magic">マジックメソッド</link>
を宣言しても問題ありません。
</para>
<note>
<para>
<link
linkend="language.oop5.decon.constructor">コンストラクタ</link>
をインターフェイスで宣言できますが、全くおすすめできません。
そうしてしまうと、
インターフェイスを実装するクラスの柔軟性が大きく損なわれる上、
コンストラクタには継承ルールが適用されないため、
一貫しない、予期せぬ結果を生む可能性があるからです。
</para>
</note>
<sect2 xml:id="language.oop5.interfaces.implements">
<title><literal>implements</literal></title>
<para>
インターフェイスを実装するには、<literal>implements</literal>
演算子を使用し、
このインターフェイスに含まれる全てのメソッドを実装する必要があります。
実装されていない場合、致命的エラーとなります。
各インターフェイスをカンマで区切って指定することで、
クラスは複数のインターフェイスを実装することができます。
</para>
<warning>
<para>
インターフェイスを実装するクラスでは、
インターフェイス内の名前とは異なる名前を
メソッドの引数に使うことができます。
しかし、PHP 8.0 以降では
<link linkend="functions.named-arguments">名前付き引数</link>
がサポートされています。
これは、メソッドをコールする側がインターフェイス内の名前に依存する可能性があるということです。
そうした理由から、開発者は実装されるインターフェイスと同じ引数名を使うことを強く推奨します。
</para>
</warning>
<note>
<para>
クラスと同様、インターフェイスも
<link linkend="language.oop5.inheritance">extends</link>
演算子で継承することができます。
</para>
</note>
<note>
<para>
インターフェイスを実装したクラスでは、
<link linkend="language.oop.lsp">シグネチャの互換性に関するルール</link>
を守った形で、インターフェイス内の全てのメソッドを宣言しなければいけません。
クラスは、同じ名前のメソッドを定義した複数のインターフェイスを実装することが出来ます。
この場合、実装されるメソッドは全て、
<link linkend="language.oop.lsp">シグネチャの互換性に関するルール</link>
に従わなければいけません。
そうすることで、<link linkend="language.oop5.variance">共変性と反変性</link>
のルールも適用できます。
</para>
</note>
</sect2>
<!-- Move this to OOP constants page? -->
<sect2 xml:id="language.oop5.interfaces.constants">
<title>定数</title>
<para>
インターフェイスに定数を持たせることもできます。
インターフェイス定数は <link linkend="language.oop5.constants">クラス定数</link>
とまったく同じように動作します。
PHP 8.1.0 より前のバージョンでは、
そのインターフェイスを継承したクラスやインターフェイスから定数をオーバーライドすることができませんでした。
</para>
</sect2>
<sect2 xml:id="language.oop5.interfaces.properties">
<title>プロパティ</title>
<simpara>
PHP 8.4.0 から、インターフェイスはプロパティを宣言できるようになりました。
宣言する場合、そのプロパティの読み取り可否、書き込み可否、あるいは双方を
指定する必要があります。
インターフェイスによる宣言は、public な読み書きに対してのみ適用できます。
</simpara>
<simpara>
クラス側でインターフェイスのプロパティ要件を満たすには、
public な通常のプロパティを定義する、
対応するフックを実装する public な
<link linkend="language.oop5.property-hooks.virtual">仮想プロパティ</link>
を定義するなど、複数の方法があります。
読み取り用プロパティは <literal>readonly</literal> プロパティによって満たすこともできます。
ただし、インターフェイスで「書き込み可能」と宣言されているプロパティを <literal>readonly</literal> にすることはできません。
</simpara>
<example>
<title>インターフェイスのでのプロパティ宣言の例</title>
<programlisting role="php">
<![CDATA[
<?php
interface I
{
// 実装クラスは、public に読み取り可能なプロパティを持つ必要があります。
// public に書き込み可能かどうかは制限されません。
public string $readable { get; }
// 実装クラスは、public に書き込み可能なプロパティを持つ必要があります。
// public に読み取り可能かどうかは制限されません。
public string $writeable { set; }
// 実装クラスは、public に読み書き可能なプロパティを持つ必要があります。
public string $both { get; set; }
}
// このクラスは、3つのプロパティをすべてフックのない通常のプロパティとして実装しています。
// これは問題ありません。
class C1 implements I
{
public string $readable;
public string $writeable;
public string $both;
}
// このクラスは、3つのプロパティすべてを、要求されたフックのみを使って実装しています。
// これも問題ありません。
class C2 implements I
{
private string $written = '';
private string $all = '';
// get フックのみ使い仮想プロパティを作成しています。
// 「public に読み取り可能」という要件を満たします。
// 書き込みはできませんが、インターフェイスからは要求されていません。
public string $readable { get => strtoupper($this->writeable); }
// インターフェイスはこのプロパティが「書き込み可能」であることのみ要求しています。
// ここに get の動作も含めることは全く問題ありません。
// この例では仮想プロパティを作成しています。
public string $writeable {
get => $this->written;
set {
$this->written = $value;
}
}
// このプロパティは「public に読み書き可能」でなければならないため、
// get と set の両方を実装する必要があります。
// デフォルトの動作のままでも問題ありません。
public string $both {
get => $this->all;
set {
$this->all = strtoupper($value);
}
}
}
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.oop5.interfaces.examples">
&reftitle.examples;
<example xml:id="language.oop5.interfaces.examples.ex1">
<title>インターフェイスの例</title>
<programlisting role="php">
<![CDATA[
<?php
// インターフェイス 'Template' を宣言する
interface Template
{
public function setVariable($name, $var);
public function getHtml($template);
}
// インターフェイスを実装する。
// これは動作します。
class WorkingTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
// これは動作しません。
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
private $vars = [];
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex2">
<title>インターフェイスの継承</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo();
}
interface B extends A
{
public function baz(Baz $baz);
}
// これは動作します。
class C implements B
{
public function foo()
{
}
public function baz(Baz $baz)
{
}
}
// これは動作せず、fatal error となります。
class D implements B
{
public function foo()
{
}
public function baz(Foo $foo)
{
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.variance.multiple.interfaces">
<title>共変性を保った形で、複数のインターフェイスを実装する</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo {}
class Bar extends Foo {}
interface A {
public function myfunc(Foo $arg): Foo;
}
interface B {
public function myfunc(Bar $arg): Bar;
}
class MyClass implements A, B
{
public function myfunc(Foo $arg): Bar
{
return new Bar();
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex3">
<title>複数のインターフェイスの継承</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo();
}
interface B
{
public function bar();
}
interface C extends A, B
{
public function baz();
}
class D implements C
{
public function foo()
{
}
public function bar()
{
}
public function baz()
{
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex4">
<title>インターフェイスでの定数</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
const B = 'Interface constant';
}
// Interface constant と表示します。
echo A::B;
class B implements A
{
const B = 'Class constant';
}
// Class constant と表示します。
// PHP 8.1.0 より前のバージョンでは、定数をオーバライドできなかったため、これは動作しませんでした。
echo B::B;
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex5">
<title>抽象クラスとインターフェイス</title>
<programlisting role="php">
<![CDATA[
<?php
interface A
{
public function foo(string $s): string;
public function bar(int $i): int;
}
// 抽象クラスは、インターフェイスの一部のみを実装しても構いません。
// 抽象クラスを継承したクラスは、未実装の残りのメソッドを実装しなければなりません。
abstract class B implements A
{
public function foo(string $s): string
{
return $s . PHP_EOL;
}
}
class C extends B
{
public function bar(int $i): int
{
return $i * 2;
}
}
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.interfaces.examples.ex6">
<title>継承と実装を同時に行う</title>
<programlisting role="php">
<![CDATA[
<?php
class One
{
/* ... */
}
interface Usable
{
/* ... */
}
interface Updatable
{
/* ... */
}
// ここでは、キーワードの順番が重要です。
// 'extends' を始めに置かなければいけません。
class Two extends One implements Usable, Updatable
{
/* ... */
}
?>
]]>
</programlisting>
</example>
<para>
インターフェイスと型宣言を組み合わせると、
特定のオブジェクトに特定のメソッドをうまく持たせることができます。
<link linkend="language.operators.type">instanceof</link> 演算子および
<link linkend="language.types.declarations">型宣言</link>
を参照ください。
</para>
</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
-->