はじめに
小文字のselfはよく使いますが、大文字のSelfというのもあります。
大文字のSelfについてまとめてみました。
型の内部で型自身へのアクセス
以下のように、Self.value
で構造体A
に紐付いたvalueにアクセスすることができます。
struct A {
static let value = 10
func printValue() {
print(Self.value)
}
}
ちなみに、Selfを書かずに直接スタティックプロパティ(value)にアクセスしようとすると
Static member 'value' cannot be used on instance of type 'A'
このようなエラーが出てきてしまいます。
スタティックメソッドでも同様です。
struct B {
var name: String
var age: Int
static func isOld(_ x: Int) -> Bool {
return x > 60
}
func ageFunc() {
print(Self.isOld(age))
}
}
ちなみに、Self
を構造体名のB
と書き換えても同じことです。ここではSelf == B
ですね。
プロトコルの定義での利用
プロトコルはそれに準拠させた型の振る舞いを事前に宣言しておくことができますが、プロトコル自身はどの型で準拠されるのかは知りません。例えるなら、車の大きさ、色、ハンドルの位置などは製造の段階で決める事ができますが、製造の段階では一体この車はどんな人が乗るのかは知りません。利用者(型)が購入(準拠)して初めてどんな人が乗るのか(プロトコル自身がどの型で準拠されるのか)を知る事ができます。
しかし、その型と同じ型のデータを返すメソッドや、同じ型同士を比較したい時もあります。その時にSelfキーワードを使います。
注意点として、前項ではその定義を含む型自身をSelfと置いていましたが、本項では少し扱い方が異なります。つまり、プロトコル定義内でSelfを用いてもプロトコルがSelfというわけではありません。ここでのSelfとは、プロトコルを採用した具体的な型 == Self
です。
protocol C {
associatedtype Element
var x: Element { get }
var y: Element { get }
func replace() -> Self
static func +(lhs: Self, rhs: Self) -> Self
}
このように、xとyを入れ替えるreplaceメソッド、左辺の右辺を加算するスタティックメソッドをprotocol Cで宣言しています。replaceメソッドの戻り値にSelfを指定しています。この時点ではどのような型に準拠するのかわからないためです。スタティックメソッド+も同様です。
構造体Dに準拠させてみましょう。
struct D: C {
typealias Element = Int
var x: Int
var y: Int
func replace() -> D {
return D(x: y, y: x)
}
static func +(lhs: D, rhs: D) -> D {
return D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
}
このように、Selfのところが具体的なかたDに変わったのがわかります。このプロトコルCは他の型にも紐付く事ができるようにするため、できるだけ具体的な型はプロトコル宣言時に書きたくないためです。(associatedtypeもそのため)
クラス内での利用
プロトコルとは違い、メソッドの返り値の型としてのみ扱う事ができます。
class E {
func someFunc() -> Self {
return self
}
}
エクステンションでの利用
前述の通り、プロトコルを準拠する型をSelfで参照できますが、一定の条件を満たす型のみプロトコルエクステンションを有効にしたいこともあります。
そのような条件を記述するときにSelfでその型を表す事ができます。
protocol SomeProtocol { }
class SomeClass { }
extension SomeProtocol where Self: SomeClass {
func someFunc() {
print(type(of: self))
}
}
class SomeSubClass: SomeClass, SomeProtocol { }
let someSubClass = SomeSubClass()
someSubClass.someFunc()
この例では、SomeProtocol
を準拠する型が条件Self: SomeClass
つまり、SomeClassを継承している場合のみsomeFunc()
を利用する事ができます。
以下のように、SomeProtocol
に準拠していてもSomeSubClass2
はSomeClass
を継承していない(条件を満たしていない)ため、someFunc()
にアクセスしようとするとエラーが出てきてしまいます。
class SomeSubClass2: SomeProtocol { }
let someSubClass2 = SomeSubClass2()
someSubClass2.someFunc()
エラー
Referencing instance method 'someFunc()' on 'SomeProtocol' requires that 'SomeSubClass2' inherit from 'SomeClass'
おわりに
selfと違ってSelfはややこしいですね。