Для любого типа
T
,
T.Type
- это метатип (metatype), то есть тип, объекты которого предоставляют описание типа
T
. Статические функции и
init
-функции
T
становятся функциями-членами
T.Type
. Пример:
struct Duck {
static func kind() -> String { return "Bird" }
init() { }
func talk() { print("Quack") }
}
let meta: Duck.Type = Duck.self
meta.kind() //=> Bird
let obj: Duck = meta.init()
obj.talk() //=> Quack
Существует 2 класса метатипов, но на практике встречаются existential metatypes, то есть их объекты могут описывать как данный тип
T
, так и любой наследник (subtype) типа
T
. Другими словами, множество объектов типа
T.Type
- это
U.self
для всех типов
U: T
. Пример:
class Animal { class var name: String { return "Animal" } }
class Cat: Animal { override class var name: String { return "Cat" } }
class Dog: Animal { override class var name: String { return "Dog" } }
var meta: Animal.Type
meta = Cat.self
meta.name //=> Cat
meta = Dog.self
meta.name //=> Dog
Или, например, можно представить себе такую функцию генерации животных:
class Animal { ... }
func createAnimal(_ type: Animal.Type) -> Animal {
if type == Cat.self {
return Cat(...)
} else if type == Dog.self {
return Dog(...)
} else {
fatalError()
}
}
На практике, такая функциональность метатипов используется редко. Как правило, они служат для явной специализации шаблонных функций. Например:
func unsafeCast<T, U>(_: T, to: U.Type) -> U
Здесь единственный способ указать тип U при вызове - это передать "фиктивный" параметр тип
U.Type
, так как аргументов типа
U
функция не принимает. Вернее, это лучший и принятый в Swift способ. Можно было бы выкрутиться вот так:
func unsafeCast<T, U>(_: T, sameAs: U) -> U
Но, понятно, что для этого нужно иметь под рукой объект типа U, и это не идеальный вариант. Жаль, что нельзя писать просто
unsafeCast<U>(t)
при вызове.
При использовании метатипа
U.Type
таким образом, его значение, как правило, игнорируется. То есть даже если вы передадите туда метатип наследника типа U, то функция всё равно будет работать с U.
P.S. Есть ещё много недокументированных фич метатипов в Swift, но там всё не очень логично и продуманно (даже разработчики из Core team не всё там понимают), так что на практике лучше ограничиться применением для явной специализации функций, как показано выше.