##プロトコルとは?
プロトコルとは日本語で議定書を意味し、クラスや構造体が実装するプロパティとメソッドなどの最低限の決まり事を設定する機能です。
JavaやC#のインターフェースにあたります。プロトコルを適用したクラスや構造体は、プロトコルに定義されているメソッド・プロパティを必ず実装しないといけません。
##プロトコルのメリット
####・実装を後から変更できる。
プロトコルの定義だけしておき、そのクラスや構造体ごとにプロパティやメソッドをを使用する箇所は変更することなく、処理を変えたい場合に有効です。
####・構造体を使用することができる
似た処理を行うクラスを複数定義する場合、スーパークラスと呼ばれる元となるクラスを作成し、それを継承するそれぞれのクラスで異なる部分を実装するという方法があります。しかしSwiftの構造体では継承を行うことができません。その代わりにプロトコルを用いることで同じようなことが構造体に実現できるのです。
####・複数のプロトコルを適用することができる
クラスは一つのクラスしか継承できませんが、プロトコルは複数適用することが可能です。日本語訳は議定書という意味にある通り、例えばある国が他国と複数の条約や決まり事を結ぶようにプロトコルも複数結ぶことができるということです。そこに関しては他言語のインターフェースと一緒です。
##プロトコルの定義
protocol プロトコル名 {
var プロパティ名: 型 { get set }
func メソッド名(引数名: 型) -> 戻り値型
}
「プロパティ名: 型」の横には{get set}という記述があります。
これはプロパティの設定をしている記述で、
{get set}は読み書き可能なプロパティ、{get}は読み込み専用プロパティを意味します。
※注意点としては{set}は定義することができません。
##プロトコルの適合
プロトコルの具体的な処理は、クラスや構造体で実装します。
クラスや構造体がプロトコルの実装を行うことを「プロトコルを適合する」と表現し、継承などとは異なります。
構造体の場合
struct 構造体名: プロトコル名 {
//処理の実装
}
クラスの場合(プロトコルの複数指定もしてます。)
class クラス名: プロトコル名, プロトコル名 {
//処理内容
}
スーパークラスを継承したクラスの場合
class クラス名: スーパークラス名, プロトコル名, プロトコル名 {
//処理内容
}
##プロトコルの使用例
//プロトコルの定義
protocol Calculation {
var num1: Double {set get}
var num2: Double {set get}
//関数に処理内容を記載しない
func calc() -> Double
}
//プロトコルの適合
struct Bmi: Calculation {
var num1: Double
var num2: Double
//処理内容を追加
func calc() -> Double {
return num1 / (num2 * num2)
}
}
let bmi = Bmi(num1: 65.0, num2:1.78 )
print(bmi.calc())
//実行結果
20
上記のコードを見ると、まず2つの数値をプロパティに持ち、計算を行うメソッドを定義したプロトコルを定義します。
その次に構造体Bmiにプロトコルを適合し、bmiの処理内容をメソッドに追加します。
そして、定数bmiに本引数を持った構造体Bmiを代入します。
結果としてprintで実行し、結果が出力されます。
##エクステンションによるプロトコルの拡張
プロトコルを拡張することで、メソッドにデフォルトの処理を実装することができます。変数の宣言で言う、初期値を設定するみたいなものです。
プロトコルを拡張するには、extention(エクステンション)という物を記述します。
※extentionはあくまでプロトコルの拡張であるため、プロトコルの定義文を書いた上で、拡張文を書かないとコンパイルエラーを起こすので注意!
extention プロトコル名 {
//処理内容
}
プログラム例
//プロトコルの定義
protocol Hello{
}
//プロトコルの拡張
extension Hello {
func hello() -> String {
return "Hello World"
}
}
//構造体の適合
struct JustEnglish: Hello {
var japanese: String
var chainese: String
//メソッドを設定しない
}
let notHello = JustEnglish ( japanese: "こんにちは 世界", chainese: "你好 世界" )
print(notHello.hello())
//実行結果
Hello World
上記のコードの通り、プロトコルを適応した構造体のメソッドを呼び出したが、初期値の処理内容が呼び出されました。このように必要でない場合は新しく処理内容を変更せず、初期の処理内容を使うこともできます。
struct JustEnglish: Hello {
var japanese: String
var chainese: String
func hello() -> String {
return japanese
}
}
//実行結果
こんにちは 世界
もちろん上記のように処理内容を変更したら、実行結果も変更されます。