Приведение типов - это способ проверки типа экземпляра или обработки этого экземпляра как другого суперкласса или подкласса где-то еще в его собственной иерархии классов.
Приведение типов в Swift реализуется с is
и as
операторами. Эти два оператора предоставляют простой и выразительный способ проверить тип значения или привести значение к другому типу.
Определение иерархии классов для приведения типов
Приведение типов с иерархией классов и подклассов можно использовать для проверки типа конкретного экземпляра класса и преобразования этого экземпляра в другой класс в той же иерархии. Три фрагмента кода ниже определяют иерархию классов и массив, содержащий экземпляры этих классов, для использования в примере приведения типов.
Первый фрагмент определяет новый базовый класс с именем MediaItem
. Этот класс предоставляет базовые функциональные возможности для любого вида элемента, который появляется в библиотеке цифрового мультимедиа. В частности, он объявляет name
свойство типа String
и инициализатор. (Предполагается, что все элементы мультимедиа, включая все фильмы и песни, будут иметь название.)init name
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
Следующий фрагмент определяет два подкласса MediaItem
. Первый подкласс Movie
содержит дополнительную информацию о фильме или фильме. Он добавляет 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 += 1
} else if item is Song {
songCount += 1
}
}
print("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
является Movie
экземпляром, и false
если это не так. Аналогичным образом проверяется, является ли элемент экземпляром. В конце - петли, значения и содержат счет того , сколько экземпляров были обнаружены каждого типа.item is Song
Song
for
in
movieCount
songCount
MediaItem
Понижающее приведение
Константа или переменная определенного типа класса может фактически ссылаться на экземпляр подкласса за кулисами. Если вы считаете, что это так, вы можете попытаться выполнить переход к типу подкласса с помощью оператора приведения типа ( as?
или as!
).
Поскольку принижение может быть неудачным, оператор приведения типов может быть двух разных форм. Условная форма, as?
возвращает необязательное значение типа, который вы пытаетесь уменьшить. Принудительная форма, as!
пытается выполнить снижение и принудительно разворачивает результат как одно сложное действие.
Используйте условную форму оператора приведения типа ( as?
), если вы не уверены, что преобразование будет выполнено успешно. Эта форма оператора всегда будет возвращать необязательное значение, и это значение будет, nil
если снижение было невозможно. Это позволяет вам проверять успешное снижение.
Используйте форсированную форму оператора приведения типа ( as!
) только в том случае, если вы уверены, что снижение всегда будет успешным. Эта форма оператора вызовет ошибку времени выполнения, если вы попытаетесь снизить класс до неправильного типа.
В приведенном ниже пример итерации по каждому MediaItem
в library
, и выводит соответствующее описание для каждого пункта. Для этого ему необходимо получить доступ к каждому элементу как true Movie
или Song
, а не просто как MediaItem
. Это необходимо для того, чтобы иметь возможность получить доступ к director
или artist
свойству Movie
или Song
для использования в описании.
В этом примере каждый элемент в массиве может быть Movie
или может быть Song
. Вы не знаете заранее, какой фактический класс использовать для каждого элемента, и поэтому целесообразно использовать условную форму оператора приведения типа ( as?
), чтобы каждый раз проверять принижение в цикле:
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("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
как Movie
. Поскольку item
это MediaItem
пример, возможно, что это может быть Movie
; в равной степени возможно также, что это может быть Song
или даже просто база MediaItem
. Из-за этой неопределенности as?
форма оператора приведения типа возвращает необязательноезначение при попытке перехода к типу подкласса. Результат имеет тип или «необязательный ».item as? Movie
Movie?
Movie
Снижение до Movie
сбоя при применении к Song
экземплярам в массиве библиотеки. Чтобы справиться с этим, в приведенном выше примере используется необязательная привязка, чтобы проверить, действительно ли необязательная Movie
содержит значение (то есть, чтобы выяснить, прошел ли переход вниз). Эта необязательная привязка записывается как « », которую можно прочитать как:if let movie = item as? Movie
«Попробуй доступ item
как Movie
. Если это успешно, установите новую временную константу, вызываемую movie
к значению, сохраненному в возвращаемом дополнительном Movie
. ”
Если даункастинг завершается успешно, свойства movie
затем используются для печати описания этого Movie
экземпляра, включая его имя director
. Аналогичный принцип используется для проверки Song
экземпляров и для печати соответствующего описания (включая artist
имя) всякий раз, когда Song
в библиотеке находится a .
ЗАМЕТКА
Кастинг фактически не изменяет экземпляр или его значения. Базовый экземпляр остается прежним; он просто обрабатывается и доступен как экземпляр типа, к которому он был приведен.
Приведение типов для Any и AnyObject
Swift предоставляет два специальных типа для работы с неспецифическими типами:
Any
может представлять экземпляр любого типа вообще, включая типы функций.AnyObject
может представлять экземпляр любого типа класса.
Используйте Any
и AnyObject
только тогда, когда вам явно нужно поведение и возможности, которые они предоставляют. Всегда лучше определиться с типами, с которыми вы ожидаете работать в своем коде.
Вот пример использования 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
значения, а String
значение, кортеж типа , фильм «Гостбастерс», а выражение закрытия , которая принимает значение и возвращает другое значение.(Double, Double)
String
String
Чтобы обнаружить конкретный тип константы или переменной, которая, как известно, имеет тип Any
или AnyObject
, вы можете использовать шаблон is
или as
в switch
случаях оператора. Приведенный ниже пример перебирает элементы в things
массиве и запрашивает тип каждого элемента с помощью switch
оператора. Несколько случаев switch
оператора связывают их совпадающее значение с константой указанного типа, чтобы позволить печатать его значение:
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("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
ЗАМЕТКА
Any
Тип представляет значения любого типа, в том числе дополнительных типов. Swift выдает предупреждение, если вы используете необязательное значение, когдаAny
ожидается значение типа . Если вам действительно нужно использовать необязательное значение в качествеAny
значения, вы можете использоватьas
оператор для явного приведения необязательного значения кAny
, как показано ниже.
let optionalNumber: Int? = 3
things.append(optionalNumber) // Warning
things.append(optionalNumber as Any) // No warning
0 комментариев