Spec-Zone.ru › Swift Language

Приведение типов

Определите тип значения во время выполнения и предоставьте более конкретную информацию о типе.

Приведение типов — это способ проверить тип экземпляра или обработать этот экземпляр как другой суперкласс или подкласс из другого места в собственной иерархии классов.

Приведение типов в Swift реализуется с помощью операторов is и as. Эти два оператора обеспечивают простой и выразительный способ проверки типа значения или приведения значения к другому типу.

Вы также можете использовать приведение типов для проверки, соответствует ли тип протоколу, как описано в <doc:Протоколы#Проверка-соответствия-протоколу>.

Определение иерархии классов для приведения типов

Вы можете использовать приведение типов с иерархией классов и подклассов для проверки типа конкретного экземпляра класса и приведения этого экземпляра к другому классу в той же иерархии. Три фрагмента кода ниже определяют иерархию классов и массив, содержащий экземпляры этих классов, для использования в примере приведения типов.

Первый фрагмент определяет новый базовый класс, называемый 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 и выводится соответствующее описание для каждого элемента. Для этого необходимо обращаться к каждому элементу как к истинному 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. Из-за этой неопределённости условная форма оператора приведения типа возвращает необязательное значение при попытке преобразования вниз к типу подкласса. Результат item as? Movie имеет тип Movie? или «необязательный Movie».

Преобразование вниз к типу Movie завершается неудачей при применении к экземплярам Song в массиве библиотеки. Для решения этой проблемы в примере выше используется привязка к необязательному значению для проверки, содержит ли необязательное значение Movie фактическое значение (то есть, для определения, было ли преобразование вниз успешным). Эта привязка к необязательному значению записывается как «if let movie = item as? Movie», что можно прочитать как:

«Попробуйте получить доступ к item как к Movie. Если это успешно, установите новую временную константу movie в значение, хранящееся в возвращаемом необязательном значении Movie».

Если преобразование вниз было успешным, свойства movie используются для вывода описания экземпляра Movie, включая имя его director. Аналогичный принцип используется для проверки экземпляров класса Song и вывода соответствующего описания (включая имя artist) всякий раз, когда в библиотеке обнаруживается экземпляр класса Song.

Примечание: Приведение типов не изменяет экземпляр или его значения. Базовый экземпляр остается неизменным; он просто обрабатывается и используется как экземпляр типа, к которому он был приведён.

Приведение типов для 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, вы можете явно привести необязательное значение к типу Any с помощью оператора as, как показано ниже.

let optionalNumber: Int? = 3
things.append(optionalNumber)        // Warning
things.append(optionalNumber as Any) // No warning

This source file is part of the Swift.org open source project
Copyright © 2014 - 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API