Преобразование типа
Преобразование типа является способом проверить тип экземпляра и/или обработать тот экземпляр, как будто это - различный суперкласс или подкласс от где-то в другом месте в его собственной иерархии классов.
Преобразование типа в Swift реализовано с is
и as
операторы. Эти два оператора обеспечивают простой и выразительный способ проверить тип значения или бросить значение к другому типу.
Можно также использовать преобразование типа, чтобы проверить, соответствует ли тип протоколу, как описано в Проверке Соответствие Протокола.
Определение иерархии классов для преобразования типа
Можно использовать преобразование типа с иерархией классов и подклассов, чтобы проверить тип определенного экземпляра класса и бросить тот экземпляр к другому классу в той же иерархии. Эти три фрагмента кода ниже определяют иерархию классов и массива, содержащего экземпляры тех классов для использования в примере преобразования типа.
Первый отрывок определяет новый вызванный базовый класс MediaItem
. Этот класс обеспечивает основную функциональность для любого вида элемента, появляющегося в библиотеке цифровых сред. В частности это объявляет a name
свойство типа String
, и init name
инициализатор. (Предполагается, что все элементы носителей, включая все фильмы и песни, будут иметь имя.)
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
Следующий отрывок определяет два подкласса MediaItem
. Первый подкласс, Movie
, инкапсулирует дополнительную информацию о фильме или фильме. Это добавляет a director
свойство поверх основы MediaItem
класс, с соответствующим инициализатором. Второй подкласс, Song
, добавляет artist
свойство и инициализатор поверх базового класса:
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
Заключительный отрывок создает постоянный вызванный массив library
, который содержит два Movie
экземпляры и три Song
экземпляры. Тип library
массив выведен путем инициализации его с содержанием литерала массивов. Средство проверки типа Swift в состоянии вывести это Movie
и Song
имейте общий суперкласс MediaItem
, и таким образом, это выводит тип [MediaItem]
для library
массив:
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]
Элементы, сохраненные в library
тихи Movie
и Song
экземпляры негласно. Однако, если Вы выполняете итерации по содержанию этого массива, элементы, которые Вы получаете назад, вводятся как MediaItem
, и не как Movie
или Song
. Для работы с ними как их собственный тип необходимо проверить их тип, или нисходящий их к другому типу, как описано ниже.
Проверка типа
Используйте оператора проверки типа (is
) проверять, имеет ли экземпляр определенный тип подкласса. Оператор проверки типа возвращается true
если экземпляр имеет тот тип подкласса и false
если это не.
Пример ниже определяет две переменные, movieCount
и songCount
, которые считают число Movie
и Song
экземпляры в library
массив:
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
++movieCount
} else if item is Song {
++songCount
}
}
println("Media library contains \(movieCount) movies and \(songCount) songs")
// prints "Media library contains 2 movies and 3 songs"
Этот пример выполняет итерации через все элементы в library
массив. На каждой передаче, for
-in
цикл устанавливает item
постоянный к следующему MediaItem
в массиве.
item is Movie
возвраты true
если ток MediaItem
a Movie
экземпляр и false
если это не. Точно так же item is Song
проверки, является ли элемент a Song
экземпляр. В конце for
-in
цикл, значения movieCount
и songCount
содержите количество сколько MediaItem
экземпляры были найдены каждого типа.
Downcasting
Константа или переменная определенного типа класса могут фактически относиться к экземпляру подкласса негласно. Где Вы верите дело обстоит так, можно попробовать к нисходящему к типу подкласса с оператором броска типа (as?
или as!
).
Поскольку downcasting может перестать работать, оператор броска типа приезжает в два различных форм. Условная форма, as?
, возвращает дополнительное значение типа, к которому Вы пробуете к нисходящему. Принудительная форма, as!
, делает попытку нисходящего, и сила - разворачивает результат как единственное составное действие.
Используйте условную форму оператора броска типа (as?
) когда Вы не будете уверены, успешно выполнится ли нисходящее. Эта форма оператора будет всегда возвращать дополнительное значение, и значение будет nil
если нисходящее не было возможно. Это позволяет Вам проверить на успешное нисходящее.
Используйте принудительную форму оператора броска типа (as!
) только, когда Вы уверены, что будет всегда успешно выполняться нисходящее. Если Вы попробуете к нисходящему к неправильному типу класса, эта форма оператора инициирует ошибку периода выполнения.
Пример ниже выполняет итерации по каждому MediaItem
в library
, и распечатывает надлежащее описание для каждого элемента. Чтобы сделать это, это должно получить доступ к каждому элементу как к истине Movie
или Song
, и не так же, как a MediaItem
. Это необходимо для него, чтобы быть в состоянии получить доступ director
или artist
свойство a Movie
или Song
для использования в описании.
В этом примере каждый элемент в массиве мог бы быть a Movie
, или это мог бы быть a Song
. Вы не знаете заранее, какой фактический класс использовать для каждого элемента, и таким образом, является надлежащим использовать условную форму оператора броска типа (as?
) проверять нисходящее каждый раз через цикл:
for item in library {
if let movie = item as? Movie {
println("Movie: '\(movie.name)', dir. \(movie.director)")
} else if let song = item as? Song {
println("Song: '\(song.name)', by \(song.artist)")
}
}
// Movie: 'Casablanca', dir. Michael Curtiz
// Song: 'Blue Suede Shoes', by Elvis Presley
// Movie: 'Citizen Kane', dir. Orson Welles
// Song: 'The One And Only', by Chesney Hawkes
// Song: 'Never Gonna Give You Up', by Rick Astley
Пример запускается путем попытки к нисходящему тока item
как a Movie
. Поскольку item
a MediaItem
экземпляр, возможно, что это мог бы быть a Movie
; одинаково, также возможно, что это мог бы быть a Song
, или даже просто основа MediaItem
. Из-за этой неопределенности, as?
форма типа бросила возвраты оператора дополнительное значение при попытке к нисходящему к типу подкласса. Результат item as? Movie
имеет тип Movie?
, или “дополнительный Movie
”.
Downcasting к Movie
сбои, когда применено к Song
экземпляры в массиве библиотеки. Для разрешения с этим пример выше использует дополнительную привязку, чтобы проверить ли дополнительное Movie
фактически содержит значение (т.е. чтобы узнать, успешно выполнилось ли нисходящее.) Эта дополнительная привязка записана “if let movie = item as? Movie
”, который может быть считан как:
“Попытайтесь получить доступ item
как a Movie
. Если это успешно, установите новую временную вызванную константу movie
к значению, сохраненному в возвращенном дополнительном Movie
.”
Если downcasting успешно выполняется, свойства movie
тогда используются для печати описания для этого Movie
экземпляр, включая имя director
. Подобный принцип используется для проверки на Song
экземпляры, и распечатать надлежащее описание (включая artist
имя) каждый раз, когда a Song
найден в библиотеке.
Преобразование типа для любого и AnyObject
Swift обеспечивает два специальных псевдонима типа для работы с неопределенными типами:
AnyObject
может представлять экземпляр любого типа класса.Any
может представлять экземпляр любого типа вообще, включая функциональные типы.
AnyObject
При работе с Какао APIs распространено получить массив с типом [AnyObject]
, или “массив значений любого типа объекта”. Это вызвано тем, что Objective C не имеет явно типизированных массивов. Однако можно часто быть уверены в типе объектов, содержавшихся в таком массиве только от информации, которую Вы знаете о API, обеспечившем массив.
В этих ситуациях можно использовать принудительную версию оператора броска типа (as
) к нисходящему каждый элемент в массиве к более определенному типу класса, чем AnyObject
, без потребности в дополнительном разворачивании.
Пример ниже определяет массив типа [AnyObject]
и заполняет этот массив с тремя экземплярами Movie
класс:
let someObjects: [AnyObject] = [
Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
Movie(name: "Moon", director: "Duncan Jones"),
Movie(name: "Alien", director: "Ridley Scott")
]
Поскольку этот массив, как известно, содержит только Movie
экземпляры, Вы можете нисходящий и разворачивать непосредственно к обязательному Movie
с принудительной версией типа бросает оператора (as!
):
for object in someObjects {
let movie = object as! Movie
println("Movie: '\(movie.name)', dir. \(movie.director)")
}
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
// Movie: 'Moon', dir. Duncan Jones
// Movie: 'Alien', dir. Ridley Scott
Для еще более короткой формы этого цикла, нисходящего someObjects
выстройте к типу [Movie]
вместо downcasting каждый элемент:
for movie in someObjects as! [Movie] {
println("Movie: '\(movie.name)', dir. \(movie.director)")
}
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
// Movie: 'Moon', dir. Duncan Jones
// Movie: 'Alien', dir. Ridley Scott
Любой
Вот пример использования Any
работать с соединением различных типов, включая функциональные типы и нетипы классов. Пример создает вызванный массив things
, который может сохранить значения типа Any
:
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
things
массив содержит два Int
значения, два Double
значения, a String
значение, кортеж типа (Double, Double)
, «Охотники за привидениями» фильма и выражение закрытия, берущее a String
оцените и возвращает другого String
значение.
Можно использовать is
и as
операторы в a switch
случаи оператора для обнаружения определенного типа константы или переменный, который, как известно, только имеет тип Any
или AnyObject
. Пример ниже выполняет итерации по элементам в things
массив и запросы тип каждого элемента с a switch
оператор. Несколько из switch
случаи оператора обязывают свое соответствующее значение с константой указанного типа позволять его значению быть распечатанным:
for thing in things {
switch thing {
case 0 as Int:
println("zero as an Int")
case 0 as Double:
println("zero as a Double")
case let someInt as Int:
println("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
println("a positive double value of \(someDouble)")
case is Double:
println("some other double value that I don't want to print")
case let someString as String:
println("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
println("an (x, y) point at \(x), \(y)")
case let movie as Movie:
println("a movie called '\(movie.name)', dir. \(movie.director)")
case let stringConverter as String -> String:
println(stringConverter("Michael"))
default:
println("something else")
}
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called 'Ghostbusters', dir. Ivan Reitman
// Hello, Michael