Преобразование типов
В прошлом уроке тип был формой коробки. Но бывает, что значение лежит в коробке одной формы, а нужно оно в другой. Тогда его перекладывают, и в Swift это всегда делают явно, отдельной записью в коде.
Вот задача из магазина: товар стоит 2.5, вы берёте 3 штуки. Количество — целое число, цена — дробное. Чтобы их перемножить, количество сначала перекладывают в коробку для дробных чисел:
let count = 3
let price = 2.5
let total = Double(count) * price
print(total) // 7.5Давайте разберём пример по строкам:
countполучает типInt: у числа3нет точки.priceполучает типDouble.Double(count)берёт3изcountи делает из него дробное3.0. Оба множителя теперьDouble, и3.0 * 2.5даёт7.5.print(total)печатает7.5.
Что будет, если убрать Double(…)?
print(count * price) // ✗error: binary operator '*' cannot be applied to operands of type 'Int' and 'Double'Знак * умеет умножать Int на Int и Double на Double, но не Int на Double. Swift не переводит одно в другое сам, даже когда это кажется безопасным.
Этот вопрос часто задают на собеседовании: «Есть ли в Swift неявное преобразование числовых типов?» Ответ — нет. Значения разных числовых типов не смешиваются в одной операции, пока одно из них не преобразуют явно.
Явное преобразование
Преобразование типа записывают как имя нужного типа и значение в скобках. Запись Double(count) состоит из двух частей:
Double— тип, в который перекладывают значение;(count)— значение, которое перекладывают.
Результат — новое значение нужного типа. Сама count при этом не меняется и остаётся Int.
Три направления
Из Int в Double
Double(…) делает из целого числа дробное, и ничего не теряется: 3 становится 3.0. Это направление выбирают, когда в расчёте участвует дробное число, как цена в примере выше.
Из Double в Int
В коробке для целых чисел нет места для дробной части. Int(…) отбрасывает её, а не округляет. У отрицательного числа она отбрасывается так же, и результат оказывается ближе к нулю:
let height = 2.9
let temperature = -2.9
print(Int(height), Int(temperature)) // 2 -2Int(height)отрезает дробную часть от2.9и даёт2, а не3.Int(temperature)отрезает дробную часть от-2.9и даёт-2, а не-3.
Здесь картинка с коробками перестаёт работать. Вещь, которая не влезла в коробку, остаётся лежать рядом целой. А дробная часть после Int(…) пропадает насовсем, и Swift не предупреждает об этом ни при компиляции, ни при запуске.
Из числа в строку
String(…) превращает число в текст: String(count) даёт строку "3", а String(price) — строку "2.5". Печатаются число и строка одинаково, но для программы это разные вещи. С числом считают, а текст можно приклеить к другому тексту: "Count: " + String(count) даёт Count: 3.
Числа, записанные в коде
Число, записанное прямо в коде, получает тип по месту. В let count = 3 оно становится Int. А в print(3 * 2.5) рядом стоит дробное число, поэтому Swift сразу делает 3 дробным, и print печатает 7.5. У константы count такой свободы нет: её тип Int выбран при объявлении.
Частая ошибка
Преобразование обернули вокруг всего умножения:
print(Double(count * price)) // ✗error: binary operator '*' cannot be applied to operands of type 'Int' and 'Double'Компилятор сначала проверяет то, что стоит в скобках: count * price. Там по-прежнему Int и Double, а умножать их друг на друга * не умеет. Поэтому программа не компилируется, и Double(…) снаружи не помогает. Преобразуйте само значение count: Double(count) * price.
Главное
- Swift не преобразует типы сам:
IntиDoubleне смешиваются в одной операции. - Преобразование пишут как имя типа со значением в скобках:
Double(count), а самаcountостаётсяInt. Int(…)отбрасывает дробную часть, а не округляет:Int(2.9)— это2, аInt(-2.9)—-2.String(…)превращает число в текст, который можно приклеить к другому тексту.