|
1 |
| -# Opaque 型\(Opaque Types\) |
| 1 | +# Opaque 型とBox 型\(Opaque Types and Boxed Types\) |
2 | 2 |
|
3 |
| -最終更新日: 2022/12/3 |
| 3 | +最終更新日: 2023/5/28 |
4 | 4 | 原文: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html
|
5 | 5 |
|
6 | 6 | 値の型に関する実装の詳細を隠す。
|
7 | 7 |
|
8 |
| -戻り値に不透明な型\(以下_Opaque 型_\)を持つ関数またはメソッドは、その戻り値の型情報を隠します。関数の戻り値の型として具体的な型を提供する代わりに、準拠するプロトコルの観点から戻り値を記述します。型情報を隠すことで、戻り値の基になる型を private のままにすることができるため、モジュールとモジュールを呼び出すコードの間に境界を設けることに役立ちます。プロトコル型の値を返すのとは異なり、Opaque 型は具体的な型情報を保持し続けます。コンパイラは型情報にアクセスできますが、モジュールのクライアントはアクセスできません。 |
| 8 | +Swift は値の型情報を隠すために 2 つの方法を提供します。不透明な型\(以下_Opaque 型_\)と Box 型です。 |
| 9 | +型情報を隠すことで、戻り値の基になる型を private のままにすることができるため、モジュールとモジュールを呼び出すコードの間に境界を設けることに役立ちます。戻り値に Opaque 型を持つ関数またはメソッドは、その戻り値の型情報を隠します。関数の戻り値の型として具体的な型を提供する代わりに、準拠するプロトコルの観点から戻り値を記述します。 |
| 10 | +コンパイラは型情報にアクセスできますが、モジュールのクライアントはアクセスできません。 |
| 11 | +Box プロトコル型は、任意の型のインスタンスを格納することができます。Box プロトコル型は、与えられたプロトコルに準拠する任意の型のインスタンスを格納することができます。 |
| 12 | +ボックス型プロトコル型は型の同一性を保持しません。つまり、値の特定の型は実行時までわからず、また、異なる値が保存されるため、時間の経過とともに変化する可能性があります。 |
9 | 13 |
|
10 | 14 | ## <a id="the-problem-that-opaque-types-solve">Opaque 型が解決する問題\(The Problem That Opaque Types Solve\)</a>
|
11 | 15 |
|
@@ -176,11 +180,52 @@ func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
|
176 | 180 |
|
177 | 181 | この場合、戻り値の基になる型は `T` によって異なります。どの形状が渡されても、`repeat(shape:count:)` はその形状の配列を作成して返します。それにもかかわらず、戻り値は常に同じ `[T]` の基になる型のため、Opaque な戻り値の型を持つ関数は単一の型の値のみを返す必要があるという要件に従います。
|
178 | 182 |
|
| 183 | +## Box プロトコル型\(Boxed Protocol Types\) |
| 184 | + |
| 185 | +Box プロトコル型は存在型とも呼ばれることがありますが、これは「`T` がプロトコルに準拠するような型 `T` が存在する」という表現に由来しています。Box プロトコル型を作るには、プロトコルの名前の前に `any` と書きます。以下はその例です: |
| 186 | + |
| 187 | +```swift |
| 188 | +struct VerticalShapes: Shape { |
| 189 | + var shapes: [any Shape] |
| 190 | + func draw() -> String { |
| 191 | + return shapes.map { $0.draw() }.joined(separator: "\n\n") |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +let largeTriangle = Triangle(size: 5) |
| 196 | +let largeSquare = Square(size: 5) |
| 197 | +let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare]) |
| 198 | +print(vertical.draw()) |
| 199 | +``` |
| 200 | + |
| 201 | +上の例では、`VerticalShapes` は、`Shape` のタイプを `[any Shape]`、つまり Box Shape 型要素の配列、と宣言しています。配列の各要素には、異なる型を入れることができ、それらの型の各々は、`Shape` プロトコルに準拠する必要があります。この実行時の柔軟性をサポートするために、Swift は必要なときに間接層を追加します。つまり、この間接的な存在は*ボックス*と呼ばれ、パフォーマンスコストがかかります。 |
| 202 | + |
| 203 | +`VerticalShapes` 型の中で、コードは `Shape` プロトコルで必要とされるメソッド、プロパティ、およびサブスクリプトを使用することができます。例えば、`VerticalShapes` の `draw()` メソッドは、配列の各要素の `draw()` メソッドを呼び出します。`Shape` が `draw()` メソッドを要件にしているため、このメソッドを利用できます。これに対して、三角形の `size` プロパティや、`Shape` が要件していないプロパティやメソッドにアクセスしようとすると、エラーが発生します。 |
| 204 | + |
| 205 | +`Shape` に使用できる 3 つのタイプを対比してみましょう: |
| 206 | + |
| 207 | +- `struct VerticalShapes<S: Shape>` と `var shapes: [S]` を書くことで、ジェネリクスを使い、ある特定の形状を要素とする配列を作り、その配列とやり取りをするすべてのコードから、その特定の型が識別できるようにする |
| 208 | +- Opaque 型を使用する場合、`var shapes:[some Shape]` と書くと、特定の形状を要素とする配列を作成し、その特定の型の識別は隠されます |
| 209 | +- Box プロトコル型を使って、`var shapes:[any Shape]` と書くと、異なる形状の要素を格納できる配列ができ、それらの型の識別は隠される |
| 210 | + |
| 211 | +この場合、Box プロトコル型は、`VerticalShapes` の呼び出し元が異なる種類の形状を一緒に混ぜることができる唯一のアプローチです。 |
| 212 | + |
| 213 | +Box 値の基礎となる型がわかっている場合は、`as` キャストを使用することができます。例えば、以下のような感じです: |
| 214 | + |
| 215 | +```swift |
| 216 | +if let downcastTriangle = vertical.shapes[0] as? Triangle { |
| 217 | + print(downcastTriangle.size) |
| 218 | +} |
| 219 | +// "5" |
| 220 | +``` |
| 221 | + |
| 222 | +より詳細は[Downcasting\(ダウンキャスト\)](../language-guide/type-casting.md#downcasting)を参照ください。 |
| 223 | + |
179 | 224 | ## Opaque 型とプロトコルの違い\(Differences Between Opaque Types and Protocol Types\)
|
180 | 225 |
|
181 |
| -Opaque 型を返すことは、プロトコル型を関数の戻り値の型として使用する場合と非常によく似ていますが、これら 2 種類の戻り値の型は、型情報を保持するかどうかが異なります。Opaque 型は 1 つの特定の型を参照しますが、関数の呼び出し側はどの型を参照するかはわかりません。プロトコル型は、プロトコルに準拠する任意の型を参照できます。一般に、プロトコル型では、格納する値の基になる型についてより柔軟に対応でき、Opaque 型を使用すると、基になる型についてより強力な保証を行うことができます。 |
| 226 | +Opaque 型を返すことは、Box プロトコル型を関数の戻り値の型として使用する場合と非常によく似ていますが、これら 2 種類の戻り値の型は、型情報を保持するかどうかが異なります。Opaque 型は 1 つの特定の型を参照しますが、関数の呼び出し側はどの型を参照するかはわかりません。Box プロトコル型は、プロトコルに準拠する任意の型を参照できます。一般に、Box プロトコル型では、格納する値の基になる型についてより柔軟に対応でき、Opaque 型を使用すると、基になる型についてより強力な保証を行うことができます。 |
182 | 227 |
|
183 |
| -例えば、Opaque 型の代わりにプロトコル型を戻り値の型として使用する、`flip(_:)` のバージョンを次に示します: |
| 228 | +例えば、Opaque 型の代わりに Box プロトコル型を戻り値の型として使用する、`flip(_:)` のバージョンを次に示します: |
184 | 229 |
|
185 | 230 | ```swift
|
186 | 231 | func protoFlip<T: Shape>(_ shape: T) -> Shape {
|
@@ -210,11 +255,11 @@ protoFlippedTriangle == sameThing // エラー
|
210 | 255 |
|
211 | 256 | この例の最終行のエラーは、いくつかの理由で発生します。当面の問題は、`Shape` のプロトコル要件に `==` 演算子が含まれていないことです。追加しようとすると、次に遭遇する問題は、`==` 演算子がその左辺と右辺の引数の型を知る必要があることです。この種の演算子は通常、`Self` 型の引数を取り、プロトコルに準拠する具体的な型に一致しますが、プロトコルに `Self` 要件を追加すると、プロトコルを型として使用することができなくなります。
|
212 | 257 |
|
213 |
| -プロトコル型を関数の戻り値の型として使用すると、プロトコルに準拠する任意の型を柔軟に返すことができます。ただし、その柔軟性によって、返された値の一部の操作が犠牲になります。この例では、プロトコル型では保持されない特定の型情報に依存しているため、`==` 演算子が使用できないことを示しています。 |
| 258 | +Box プロトコル型を関数の戻り値の型として使用すると、プロトコルに準拠する任意の型を柔軟に返すことができます。ただし、その柔軟性によって、返された値の一部の操作が犠牲になります。この例では、Box プロトコル型では保持されない特定の型情報に依存しているため、`==` 演算子が使用できないことを示しています。 |
214 | 259 |
|
215 |
| -このアプローチのもう 1 つの問題は、形状変換をネストできないことです。三角形を反転した結果は、`Shape` 型の値で、`protoFlip(_:)` 関数は、`Shape` プロトコルに準拠した何らかの型の引数を取ります。ただし、プロトコル型はそのプロトコル自体に準拠しません。`protoFlip(_:)` によって返される値は `Shape` に準拠していません。これは、反転した形状が `protoFlip(_:)` の有効な引数ではないため、複数の変換を適用する `protoFlip(protoFlip(smallTriangle))` のようなコードが無効だということを意味します。 |
| 260 | +このアプローチのもう 1 つの問題は、形状変換をネストできないことです。三角形を反転した結果は、`Shape` 型の値で、`protoFlip(_:)` 関数は、`Shape` プロトコルに準拠した何らかの型の引数を取ります。ただし、Box プロトコル型はそのプロトコル自体に準拠しません。`protoFlip(_:)` によって返される値は `Shape` に準拠していません。これは、反転した形状が `protoFlip(_:)` の有効な引数ではないため、複数の変換を適用する `protoFlip(protoFlip(smallTriangle))` のようなコードが無効だということを意味します。 |
216 | 261 |
|
217 |
| -対照的に、Opaque 型は、基になる型情報を保持します。Swift は関連型を推論できるため、プロトコル型を戻り値として使用できない場所で Opaque 型な戻り値の型を使用できます。例えば、これは [Generics\(ジェネリクス\)](generics.md)の `Container` プロトコルの別のバージョンです: |
| 262 | +対照的に、Opaque 型は、基になる型情報を保持します。Swift は関連型を推論できるため、Box プロトコル型を戻り値として使用できない場所で Opaque 型な戻り値の型を使用できます。例えば、これは [Generics\(ジェネリクス\)](generics.md)の `Container` プロトコルの別のバージョンです: |
218 | 263 |
|
219 | 264 | ```swift
|
220 | 265 | protocol Container {
|
|
0 commit comments