Что такое tostring java
Java toString() Method
Last modified: October 30, 2019
Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
1. Overview
Every class in Java is a child of the Object class either directly or indirectly. And since the Object class contains a toString() method, we can call toString() on any instance and get its string representation.
In this tutorial, we’ll look at the default behavior of toString() and learn how to change its behavior.
2. Default Behavior
Whenever we print an object reference, it invokes the toString() method internally. So, if we don’t define a toString() method in our class, then Object#toString() is invoked.
Object’s toString() method is pretty generic:
To see how this works, let’s create a Customer object that we’ll use throughout our tutorial:
Now, if we try to print our Customer object, Object#toString() will be called, and the output will be similar to:
3. Overriding Default Behavior
Looking at the above output, we can see that it doesn’t give us much information about the contents of our Customer object. Generally, we aren’t interested in knowing the hashcode of an object, but rather the contents of our object’s attributes.
By overriding the default behavior of the toString() method, we can make the output of the method call more meaningful.
Now, let’s look at a few different scenarios using objects to see how we can override this default behavior.
4. Primitive Types and Strings
Our Customer object has both String and primitive attributes. We need to override the toString() method to achieve a more meaningful output:
Let’s see what we get when we call toString() now:
5. Complex Java Objects
Let’s now consider a scenario where our Customer object also contains an order attribute that is of type Order. Our Order class has both String and primitive data type fields.
So, let’s override toString() again:
To fix that let’s override toString() in Order, too:
Now, let’s see what happens when we call the toString() method on our Customer object that contains an order attribute:
6. Array of Objects
To fix that let’s use Arrays.toString() for the orders field:
Let’s see the results of calling the above toString() method:
7. Wrappers, Collections, and StringBuffers
When an object is made up entirely of wrappers, collections, or StringBuffers, no custom toString() implementation is required because these objects have already overridden the toString() method with meaningful representations:
Let’s again see the results of calling toString():
8. Conclusion
In this article, we looked at creating our own implementations of the toString() method.
All of the source code for this article is available over on GitHub.
toString: Великий и Ужасный
Функция toString в языке JavaScript наверно самая «неявно» обсуждаемая как среди самих js-разработчиков, так и среди внешних наблюдателей. Она — причина многочисленных шуток и мемов про многие подозрительные арифметические операции, преобразования, вводящие в ступор [object Object]‘ы. Уступает, возможно, лишь удивлениям при работе с float64.
Интересные случаи, которые мне приходилось наблюдать, использовать или преодолевать, мотивировали меня написать настоящий разбор полетов. Мы галопом проскочим по спецификации языка и на примерах разберем неочевидные особенности toString.
Если вы ожидаете полезного и достаточного руководства, то вам больше подойдет этот, этот и тот материалы. Если же ваше любопытство все таки преобладает над прагматичностью, то прошу под кат.
Все что нужно знать
Функция toString — свойство объекта-прототипа Object, простыми словами — его метод. Используется при строковом преобразовании объекта и по-хорошему должна возвращать примитивное значение. Свои реализации также имеют объекты-прототипы: Function, Array, String, Boolean, Number, Symbol, Date, RegExp, Error. Если вы реализуете свой объект-прототип (класс), то хорошим тоном будет определить для него и toString.
JavaScript — язык со слабой системой типов: а значит, позволяет нам смешивать разные типы, выполняет многие операции неявно. В преобразованиях toString работает в паре с valueOf, чтобы свести объект к нужному для операции примитиву. Например, оператор сложения оборачивается конкатенацией при наличии среди операторов хотя бы одной строки. Некоторые стандартные функции языка перед своей работой приводят аргумент к строке: parseInt, decodeURI, JSON.parse, btoa и проч.
Про неявное приведение типов сказано и высмеяно уже довольно много. Мы же рассмотрим реализации toString ключевых объектов-прототипов языка.
Object.prototype.toString
Если мы обратимся к соответствующему разделу спецификации, то обнаружим, что основная задача дефолтного toString — это получить так называемый tag для конкатенации в результирующую строку:
Болеющие рефлексией сразу отметят возможность получить тип объекта простой операцией (не рекомендуется спецификацией, но можно):
Особенностью дефолтного toString является то, что он работает с любым значением this. Если это примитив, то он будет приведен к объекту (null и undefined проверяются отдельно). Никаких TypeError:
Как это может пригодиться? Например, при разработке инструментов динамического анализа кода. Имея импровизированный пул используемых в процессе работы приложения переменных, в run-time можно собирать полезную однородную статистику.
У этого подхода есть один существенный недостаток: пользовательские типы. Не трудно догадаться, что для их экземпляров мы просто получим «Object».
Кастомный Symbol.toStringTag и Function.name
ООП в JavaScript базируется на прототипах, а не на классах (как например в Java), и готового метода getClass() у нас нет. Решить возникшую проблему поможет явное определение символа toStringTag для пользовательского типа:
или в прототипном стиле:
Есть альтернативное решение через read-only свойство Function.name, которое пока не является частью спецификации, но поддерживается большинством браузеров. Каждый экземпляр объекта-прототипа/класса имеет ссылку на функцию-конструктор, с помощью которой он был создан. А значит мы можем узнать название типа:
или в прототипном стиле:
Разумеется, это решение не работает для объектов, созданных с помощью анонимной функции («anonymous») или Object.create(null), а также для примитивов без объекта-обертки (null, undefined).
Таким образом, для надежной манипуляции типами переменных стоит комбинировать известные приемы, в первую очередь отталкиваясь от решаемой задачи. В подавляющем большинстве случаев достаточно typeof и instanceof.
Function.prototype.toString
Мы немного отвлеклись, но в результате добрались до функций, у которых есть свой интересный toString. Для начала взглянем на следующий код:
Многие наверно догадались, что это пример куайна. Если загрузить скрипт с таким содержимым в тело страницы, то в консоль будет выведена точная копия исходного кода. Это происходит благодаря вызову toString от функции arguments.callee.
Используемая реализация toString объекта-прототипа Function возвращает строковое представление исходного кода функции, сохраняя используемый при ее определении синтаксис: FunctionDeclaration, FunctionExpression, ClassDeclaration, ArrowFunction и проч.
Например, мы имеем стрелочную функцию:
Вызов bind.toString() вернет нам строковое представление ArrowFunction:
А вызов toString от обернутой функции — это уже строковое представление FunctionExpression:
Этот пример с bind не случаен, так как у нас есть готовое решение с привязкой контекста Function.prototype.bind, и касательно нативных bound functions есть особенность работы Function.prototype.toString с ними. В зависимости от реализации может быть получено представление как самой обернутой функции, так и оборачиваемой (target) функции. V8 и SpiderMonkey последних версий хрома и ff:
Таким образом, стоит проявлять осторожность с нативно-декорируемыми функциями.
Практика использования f.toString
Вариантов использования рассматриваемого toString очень много, но настоятельно только в качестве инструмента метапрограммирования или дебага. Наличие подобного в бизнес-логике типового приложения рано или поздно приведет к неподдерживаемому разбитому корыту.
Самое простое, что приходит на ум — это определение длины функции:
Расположение и количество пробельных символов результата toString отдается спецификацией на откуп конкретной реализации, поэтому для чистоты мы предварительно убираем лишнее, приводя к общему виду. К слову, в старых версиях движка Gecko у функции был специальный параметр indentation, помогающий с форматированием отступов.
Сразу на ум приходит и определение имен параметров функции, что может пригодится для рефлексии:
Это коленочное решение подойдет для синтаксиса FunctionDeclaration и FunctionExpression. Если потребуется более развернутое и точное, то рекомендую обратиться за примерами к исходному коду вашего любимого фреймворка, который наверняка имеет под капотом какое-нибудь внедрение зависимостей, основанное именно на именах объявленных параметров.
Опасный и интересный вариант переопределения функции через eval:
Зная структуру исходной функции, мы создали новую, подменив используемый в ее теле перед аргументами оператор сложения — умножением. В случае программно-генерируемого кода или отсутствия интерфейса расширения функции это может быть волшебным образом полезно. Например, если вы исследуете некоторую математическую модель, подбирая подходящую функцию, играясь с операторами и коэффициентами.
Более практичное использование — это компиляция и дистрибьюция шаблонов. Многие реализации шаблонизаторов компилируют исходный текст шаблона и предоставляют функцию от данных, которая уже формирует конечный HTML (или другое). Далее на примере функции _.template:
А что, если компиляция шаблона требует аппаратных ресурсов или клиент ну очень тонкий? В этом случае мы можем компилировать шаблон на серверной стороне и отдавать клиентам уже не текст шаблона, а строковое представление готовой функции. Тем более, вам не нужно грузить на клиент библиотеку шаблонизатора.
Теперь нам необходимо выполнить этот код на клиенте перед использованием. Чтобы при компиляции не было SyntaxError из-за синтаксиса FunctionExpression:
Или как вам больше нравится. В любом случае:
Это может быть не самая лучшая практика компиляции шаблонов на серверной стороне и их дальнейшего распространения на клиенты. Просто пример с использованием связки Function.prototype.toString и eval.
Наконец, старая задача про определение имени функции (до появления свойства Function.name) через toString:
Разумеется, это хорошо работает в случае синтаксиса FunctionDeclaration. Более интеллектуальное решение потребует хитрого регулярного выражения или использования сопоставления с образцом.
В интернетах полно интересных решений на базе Function.prototype.toString, достаточно лишь поинтересоваться. Делитесь своим опытом в комментариях: очень интересно.
Array.prototype.toString
Реализация toString объекта-прототипа Array является обобщенной и может быть вызвана для любого объекта. Если объект имеет метод join, то результатом toString будет его вызов, иначе — Object.prototype.toString.
Array, логично, имеет метод join, который конкатенирует строковое представление всех своих элементов через переданный в качестве параметра separator (по умолчанию это запятая).
Допустим, нам надо написать функцию, сериализующую список своих аргументов. Если все параметры — примитивы, то во многих случаях мы можем обойтись без JSON.stringify:
Только помните, что строка ’10’ и число 10 будут сериализованы одинаково. В задаче про кратчайший мемоизатор на одном из этапом использовалось это решение.
Нативный джойн элементов массива работает через арифметический цикл от 0 до length и не фильтрует отсутствующие элементы (null и undefined). Вместо этого происходит конкатенация с separator. Это приводит к следующему:
Поэтому, если вы по той или иной причине добавляете в массив элемент с большим индексом (например, это сгенерированный натуральный id), ни в коем случае не джойните и, соответственно, не приводите к строке без предварительной подготовки. Иначе могут быть последствия: Invalid string length, out of memory или просто повисший скрипт. Используйте функции объекта Object values и keys, чтобы итерироваться только по собственным перечислимым свойствам объекта:
Но гораздо лучше избегать подобного обращения с массивом: скорее всего в качестве хранилища вам подошел бы простой key-value объект.
К слову, такая же опасность есть и при сериализации через JSON.stringify. Только еще серьезнее, так как пустые и неподдерживаемые элементы представлены уже как «null»:
Завершая раздел хотелось бы напомнить, что вы можете определить для пользовательского типа свой метод join и вызывать Array.prototype.toString.call в качестве альтернативного приведения к строке, но я сомневаюсь, что это имеет какое-то практическое применение.
Number.prototype.toString и parseInt
Одна из моих любимых задач для js-викторин — Что вернет следующий вызов parseInt?
Первое, что делает parseInt — это неявное приведение аргумента к строке через вызов абстрактной функции ToString, которая в зависимости от типа аргумента выполняет нужную ветку приведения. Для типа number осуществляется следующее:
Я не буду дублировать здесь алгоритм определения предпочтительной формы, только отмечу следующее: если количество цифр числа в десятичной записи превышает 21, то будет выбрана экспоненциальная форма. А это значит, что в нашем случае parseInt работает не с «100. 000» а с «1e30». Поэтому ответ совсем не ожидаемый 2^30. Кто знает природу этого магического числа 21 — пишите!
Далее parseInt смотрит на используемое основание системы счисления radix (по умолчанию 10, у нас — 2) и проверяет символы полученной строки на совместимость с ним. Встретив ‘e’, отсекает весь хвост, оставляя только «1». Результатом же будет целое число, полученное путем перевода из системы с основанием radix в десятичную — в нашем случае, это 1.
Здесь происходит вызов функции toString от объекта-прототипа Number, который использует тот же алгоритм приведения number к строке. Он тоже имеет опциональный параметр radix. Только он бросает RangeError для невалидного значения (должно быть целое от 2 до 36 включительно), тогда как parseInt возвращает NaN.
Стоит помнить о верхней границе системы счисления, если планируете реализовывать экзотическую хэш-функцию: вам может не подойти этот toString.
Задачка, чтобы отвлечься на минутку:
Что вернет и как исправить?
Обделенное вниманием
Мы рассмотрели toString далеко не всех даже нативных объектов-прототипов. Отчасти, потому что лично мне не приходилось попадать с ними в передряги, да и интересного в них не много. Также мы не затронули функцию toLocaleString, так как про нее хорошо бы поговорить отдельно. Если я все таки что-то зря обделил вниманием, упустил из виду или недопонял — обязательно пишите!
Призыв к бездействию
Приведенные мною примеры ни в коем случае не являются готовыми рецептами — только пищей для размышлений. Кроме того я нахожу бессмысленным и немного бестолковым обсуждать подобное на технических собеседованиях: для этого есть вечные темы про замыкания, хойстинг, event loop, паттерны модуль/фасад/медиатор и «конечно» вопросы про [используемый фреймворк].
Настоящая статья получилась сборной солянкой, и я надеюсь вы нашли что-то интересное для себя. PS Язык JavaScript — удивителен!
Бонус
Подготавливая настоящий материал к публикации, я пользовался Google Переводчиком. И совершенно случайно обнаружил занимательный эффект. Если выбрать перевод с русского на английский, ввести «toString» и начать его стирать через клавишу Backspace, то мы будем наблюдать:
Такая вот ирония! Думаю, я далеко не первый такой, но на всякий случай отправил им скриншот со сценарием воспроизведения. Выглядит как безобидный self-XSS, поэтому и делюсь.
Все методы класса Object, метод toString()
— Сегодня мы будем изучать класс Object.
Ты уже сталкивался с ним, и знаешь, что Object – базовый класс для всех классов. У него практически нет никаких данных, но есть несколько методов.
— А зачем ему методы? Разве кто-то создает его объекты?
— Посмотри на ситуацию с такой стороны – методы, которые есть у Object’а, есть вообще у всех классов. Т.е. разработчики Java отобрали несколько методов, которые, по их мнению, должны быть у всех классов и добавили их в класс Object.
А в сочетании с полиморфизмом – возможностью переопределять методы класса Object в классах-наследниках – это порождает очень мощный инструмент.
Давай посмотрим, что же это за методы:
Метод | Описание |
---|---|
Возвращает строковое представление объекта. | |
Пара методов, которые используются для сравнения объектов. | |
Возвращает специальный объект, который описывает текущий класс. | |
Методы для контроля доступа к объекту из различных нитей. Управление синхронизацией нитей. | |
Метод позволяет «освободить» родные не-Java ресурсы: закрыть файлы, потоки и т.д. | |
Метод позволяет клонировать объект: создает дубликат объекта. |
Методы можно разбить на 6 групп. С некоторыми ты уже знаком, а с остальными мы познакомимся в ближайших лекциях.
— Что-то не вижу тут ничего полезного.
— Амиго! Если бы эти методы были не важны, их бы не добавили абсолютно всем объектам! Так что советую тебе более внимательно разобраться – что это такое и зачем они нужны. Если тебе кажется, что они не важны, значит, ты что-то не понял или понял не правильно.
— Ок. Я буду внимательно слушать.
— Начнем с метода toString();
Этот метод позволяет получить текстовое описание любого объекта. Реализация его в классе Object очень простая:
getClass() и hashCode() – это тоже методы класса Object.
Вот стандартный результат вызова такого метода
— И в чем же польза такого описания?
— Из такого описания можно узнать класс объекта, у которого вызвали данный метод. А также можно различать объекты – разным объектам соответствуют разные цифры, идущие после символа @.
Но ценность данного метода в другом. Данный метод можно переопределить в любом классе и возвращать более нужное или более детальное описание объекта.
Но и это еще не все. Благодаря тому, что для каждого объекта можно получить его текстовое представление, в Java можно было реализовать поддержку «сложения» строк с объектами.
Вот смотри:
Код | Что происходит на самом деле |
---|
— Да, я постоянно этим пользуюсь. Особенно когда пишу программу или ищу в ней ошибки. Полезный метод
Метод Java toString()
Метод Java toString, метод Java Object toString (), объект tostring, метод java toString, объект tostring java, метод переопределения java toString, метод Java String toString (), пример кода метода public String toString ().
Метод Java toString-очень полезный метод, и хотя вы, возможно, этого не знаете, я уверен, что вы часто использовали его в своих программах.
Метод Java toString
И String.valueOf() реализация выглядит так:
Теперь, когда мы согласны с тем, что он часто используется, давайте начнем изучать метод toString более подробно.
Метод Java Object toString()
Давайте рассмотрим простую программу, в которой мы создадим объект java и вызовем его метод toString.
Теперь возникают два вопроса: во – первых, где реализован метод toString (), потому что я не вижу его в классе данных? Во-вторых, что это за вывод, который вряд ли содержит какую-либо значимую информацию.
Теперь понятно, почему на выходе имя класса с@, а затем какое-то шестнадцатеричное число.
Важные моменты метода Java toString()
Давайте теперь посмотрим на метод javadoc Object toString() и посмотрим, что он говорит.
Основываясь на приведенной выше рекомендации, мы почти всегда должны переопределять метод toString (), чтобы возвращать полезную информацию об объекте. Итак, давайте изменим реализацию нашего класса данных и переопределим его метод toString.
Теперь, когда вы запустите вышеуказанную программу, вывод будет <"id":10, "имя":"Java">. Теперь это имеет больше смысла для любого, кто смотрит на результат.
Важные моменты для переопределения метода toString()
Давайте рассмотрим некоторые важные моменты, которые вы должны учитывать при переопределении метода toString ().
Какой выход вы бы предпочли?
Без реализации toString () :
С реализацией toString () :
Это все для краткого обзора метода java toString ().
Java toString() Method
Last modified: October 30, 2019
Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
1. Overview
Every class in Java is a child of the Object class either directly or indirectly. And since the Object class contains a toString() method, we can call toString() on any instance and get its string representation.
In this tutorial, we’ll look at the default behavior of toString() and learn how to change its behavior.
2. Default Behavior
Whenever we print an object reference, it invokes the toString() method internally. So, if we don’t define a toString() method in our class, then Object#toString() is invoked.
Object’s toString() method is pretty generic:
To see how this works, let’s create a Customer object that we’ll use throughout our tutorial:
Now, if we try to print our Customer object, Object#toString() will be called, and the output will be similar to:
3. Overriding Default Behavior
Looking at the above output, we can see that it doesn’t give us much information about the contents of our Customer object. Generally, we aren’t interested in knowing the hashcode of an object, but rather the contents of our object’s attributes.
By overriding the default behavior of the toString() method, we can make the output of the method call more meaningful.
Now, let’s look at a few different scenarios using objects to see how we can override this default behavior.
4. Primitive Types and Strings
Our Customer object has both String and primitive attributes. We need to override the toString() method to achieve a more meaningful output:
Let’s see what we get when we call toString() now:
5. Complex Java Objects
Let’s now consider a scenario where our Customer object also contains an order attribute that is of type Order. Our Order class has both String and primitive data type fields.
So, let’s override toString() again:
To fix that let’s override toString() in Order, too:
Now, let’s see what happens when we call the toString() method on our Customer object that contains an order attribute:
6. Array of Objects
To fix that let’s use Arrays.toString() for the orders field:
Let’s see the results of calling the above toString() method:
7. Wrappers, Collections, and StringBuffers
When an object is made up entirely of wrappers, collections, or StringBuffers, no custom toString() implementation is required because these objects have already overridden the toString() method with meaningful representations:
Let’s again see the results of calling toString():
8. Conclusion
In this article, we looked at creating our own implementations of the toString() method.
All of the source code for this article is available over on GitHub.