Форматирование строк в Kotlin: как вывести числа с плавающей точкой и проценты?
Основные способы форматирования строк в Kotlin
В Kotlin есть несколько подходов к форматированию строк. Рассмотрим их от простых к более продвинутым, с акцентом на идиоматичность и читаемость кода.
1. Функция String.format() (Java-стиль)
Самый прямолинейный способ — использовать знакомую Java-функцию. Kotlin полностью поддерживает её, но есть нюанс: вы можете вызывать её как статический метод класса String или как extension-функцию для самого форматируемого объекта.
val finalPrice = 1062.50
val discount = 15
// Вариант 1: статический вызов (как в Java)
println(String.format("Цена со скидкой: %.2f", finalPrice))
// Вариант 2: extension-функция (более Kotlin-стиль)
println("%.2f".format(finalPrice)) // "1062.50"
// Форматирование нескольких аргументов
println("Скидка: %d%%".format(discount)) // "Скидка: 15%"
Обратите внимание на двойной знак процента (%%) — это стандартный способ вывести символ % в форматной строке.
2. Шаблонные строки с $ (самый идиоматичный способ)
В Kotlin предпочтительнее использовать строковые шаблоны. Они не поддерживают форматирование напрямую, но вы можете комбинировать их с функциями преобразования чисел.
// Простое выражение с долларом
println("Цена: $finalPrice") // "Цена: 1062.5" (много знаков)
// Форматирование через .format() внутри шаблона
println("Цена со скидкой: ${"%.2f".format(finalPrice)}")
println("Скидка: $discount%") // Просто добавляем символ % в конец
// Для сложных выражений используйте фигурные скобки
val percent = 15.5
println("Скидка: ${"%.1f".format(percent)}%") // "Скидка: 15.5%"
Этот способ удобен, когда нужно вставить форматированное значение прямо в середину строки без создания дополнительных переменных.
3. Использование DecimalFormat для чисел
Для более сложного форматирования чисел (например, с разделителями групп или валютами) лучше использовать java.text.DecimalFormat:
import java.text.DecimalFormat
val df = DecimalFormat("#,##0.00")
println("Цена: ${df.format(finalPrice)}") // "Цена: 1 062,50" (зависит от локали)
// Для процентов
val percentFormat = DecimalFormat("#0.##%")
println("Скидка: ${percentFormat.format(discount / 100.0)}") // "Скидка: 15%"
Обратите внимание: DecimalFormat для процентов ожидает значение в долях (0.15 для 15%), поэтому нужно делить на 100.
4. Extension-функции для удобства
Вы можете создать свои функции-расширения, чтобы сделать код ещё чище:
fun Double.toPriceFormat(): String = "%.2f".format(this)
fun Int.toPercentFormat(): String = "$this%"
// Использование
println("Цена: ${finalPrice.toPriceFormat()}")
println("Скидка: ${discount.toPercentFormat()}")
Итоговые рекомендации
- Для простых случаев — используйте шаблонные строки с
$и встроенным.format(). - Для локализованного вывода (валюты, даты) — используйте
DecimalFormatилиSimpleDateFormat. - Для консольного вывода (отладка) — можно использовать
println(String.format(...))как быстрый вариант. - Избегайте смешивания
%и$в одной строке — это ухудшает читаемость.
В вашем примере с калькулятором самый идиоматичный код будет выглядеть так:
fun main() {
val price = 1250.0
val discount = 15
val finalPrice = price * (100 - discount) / 100
println("Цена со скидкой: ${"%.2f".format(finalPrice)}")
println("Скидка: $discount%")
}
Этот код читается естественно и не требует импорта дополнительных классов.
<<Альтернативный подход: использование println с форматированием через System.out.printf
Если вы привыкли к C-стилю форматирования, в Kotlin можно напрямую использовать System.out.printf():
val finalPrice = 1062.50
val discount = 15
System.out.printf("Цена со скидкой: %.2f%n", finalPrice)
System.out.printf("Скидка: %d%%%n", discount)
Важно: System.out.printf() не добавляет перевод строки автоматически, поэтому нужно использовать %n или \n. В отличие от println, это функция для форматированного вывода, а не для создания строки.
Когда это может быть полезно?
- При миграции кода с Java на Kotlin — не нужно переписывать все форматирования.
- В консольных утилитах, где важна производительность (меньше создаётся промежуточных строк).
- При работе с
StringBuilderилиPrintWriter.
Недостатки
- Менее идиоматично для Kotlin.
- Нельзя использовать внутри строковых шаблонов.
- Требует явного указания локали для чисел с плавающей точкой (по умолчанию используется системная локаль).
В большинстве случаев я рекомендую придерживаться первого ответа — использовать .format() или шаблонные строки, так как это более естественно для Kotlin и лучше читается.