Книга: Программирование на языке Ruby
2.16. Явные и неявные преобразования
2.16. Явные и неявные преобразования
На первый взгляд, методы to_s
и to_str
могут вызвать недоумение. Ведь оба преобразуют объект в строковое представление, так?
Но есть и различия. Во-первых, любой объект в принципе можно как-то преобразовать в строку, поэтому почти все системные классы обладают методом to_s
. Однако метод to_str
в системных классах не реализуется никогда.
Как правило, метод to_str
применяется для объектов, очень похожих на строки, способных «замаскироваться» под строку. В общем, можете считать, что метод to_s
— это явное преобразование, а метод to_str
— неявное.
Я уже сказал, что ни в одном системном классе не определен метод to_str
(по крайней мере, мне о таких классах неизвестно). Но иногда они вызывают to_str
(если такой метод существует в соответствующем классе).
Первое, что приходит на ум, — подкласс класса String
; но на самом деле объект любого класса, производного от String
, уже является строкой, так что определять метод to_str
излишне.
А вот пример из реальной жизни. Класс Pathname
определен для удобства работы с путями в файловой системе (например, конкатенации). Но путь естественно отображается на строку (хотя и не наследует классу String
).
require 'pathname'
path = Pathname.new("/tmp/myfile")
name = path.to_s # "/tmp/myfile"
name = path.to_str # "/tmp/myfile" (Ну и что?)
# Вот где это оказывается полезно...
heading = "Имя файла равно " + path
puts heading# " Имя файла равно /tmp/myfile"
В этом фрагменте мы просто дописали путь в конец обычной строки "Имя файла равно"
. Обычно такая операция приводит к ошибке во время выполнения, поскольку оператор +
ожидает, что второй операнд — тоже строка. Но так как в классе Pathname
есть метод to_str
, то он вызывается. Класс Pathname
«маскируется» под строку, то есть может быть неявно преобразован в String
.
На практике методы to_s
и to_str
обычно возвращают одно и то же значение, но это необязательно. Неявное преобразование должно давать «истинное строковое значение» объекта, а явное можно расценивать как «принудительное» преобразование.
Метод puts
обращается к методу to_s
объекта, чтобы получить его строковое представление. Можно считать, что это неявный вызов явного преобразования. То же самое справедливо в отношении интерполяции строк. Вот пример:
class Helium
def to_s
"He"
end
def to_str
"гелий"
end
end
e = Helium.new
print "Элемент "
puts e # Элемент He.
puts "Элемент " + e # Элемент гелий.
puts "Элемент #{e}" # Элемент He.
Как видите, разумное определение этих методов в собственном классе может несколько повысить гибкость применения. Но что сказать об идентификации объектов, переданных методам вашего класса?
Предположим, например, что вы написали метод, который ожидает в качестве параметра объект String
. Вопреки философии «утипизации», так делают часто, и это вполне оправдано. Например, предполагается, что первый параметр метода File.new
— строка.
Решить эту проблему просто. Если вы ожидаете на входе строку, проверьте, имеет ли объект метод to_str
, и при необходимости вызывайте его.
def set_title(title)
if title.respond_to? :to_str
title = title.to_str
end
# ...
end
Ну а если объект не отвечает на вызов метода to_str
? Есть несколько вариантов действий. Можно принудительно вызвать метод to_s
; можно проверить, принадлежит ли объект классу String
или его подклассу; можно, наконец, продолжать работать, понимая, что при попытке выполнить операцию, которую объект не поддерживает, мы получим исключение ArgumentError
.
Короткий путь к цели выглядит так:
title = title.to_str rescue title
Он опирается на тот факт, что при отсутствии реализации метода to_str
возникнет исключение. Разумеется, модификаторы rescue
могут быть вложенными:
title = title.to_str rescue title.to_s rescue title
# Обрабатывается маловероятный случай, когда отсутствует даже метод to_s.
С помощью неявного преобразования можно было бы сделать строки и числа практически эквивалентными:
class Fixnum
def to_str
self.to_s end
end
str = "Число равно " + 345 # Число равно 345.
Но я не рекомендую так поступать: «много хорошо тоже нехорошо». В Ruby, как и в большинстве языков, строки и числа — разные сущности. Мне кажется, что ясности ради преобразования, как правило, должны быть явными.
И еще: в методе to_str
нет ничего волшебного. Предполагается, что он возвращает строку, но если вы пишете такой метод сами, ответственность за то, что он действительно так и поступает, ложится на вас.
- 2.1. Представление обычных строк
- 2.2. Альтернативная нотация для представления строк
- 2.3. Встроенные документы
- 2.4. Получение длины строки
- 2.5. Построчная обработка
- 2.6. Побайтовая обработка
- 2.7. Специализированное сравнение строк
- 2.8. Разбиение строки на лексемы
- 2.9. Форматирование строк
- 2.10. Строки в качестве объектов ввода/вывода
- 2.11. Управление регистром
- 2.12. Вычленение и замена подстрок
- 2.13. Подстановка в строках
- 2.14. Поиск в строке
- 2.15. Преобразование символов в коды ASCII и обратно
- 2.16. Явные и неявные преобразования
- 2.17. Дописывание в конец строки
- 2.18. Удаление хвостовых символов новой строки и прочих
- 2.19. Удаление лишних пропусков
- 2.20. Повтор строк
- 2.21. Включение выражений в строку
- 2.22. Отложенная интерполяция
- 2.23. Разбор данных, разделенных запятыми
- 2.24. Преобразование строки в число (десятичное или иное)
- 2.25. Кодирование и декодирование строк в кодировке rot13
- 2.26. Шифрование строк
- 2.27. Сжатие строк
- 2.28. Подсчет числа символов в строке
- 2.29. Обращение строки
- 2.30. Удаление дубликатов
- 2.31. Удаление заданных символов
- 2.32. Печать специальных символов
- 2.33. Генерирование последовательности строк
- 2.34. Вычисление 32-разрядного CRC
- 2.35. Вычисление МD5-свертки строки
- 2.36. Вычисление расстояния Левенштейна между двумя строками
- 2.37. base64-кодирование и декодирование
- 2.38. Кодирование и декодирование строк (uuencode/uudecode)
- 2.39. Замена символов табуляции пробелами и сворачивание пробелов в табуляторы
- 2.40. Цитирование текста
- 2.41. Заключение
- Преобразования типов
- Явные преобразования типов
- Разные преобразования, помогающие поддержать рассказ
- Корневые элементы преобразования
- Глава 4 Структура преобразования
- Явные и неявные свободные звенья
- 5.15. Явные и неявные преобразования чисел
- 2.7 Преобразования типов
- Модификаторы спецификации преобразования, используемые в функции printf( )
- А6.5. Арифметические преобразования
- Преобразования типов при присваивании
- 6.4.3. Явные критерии