Постоянно рекомендуется переопределять (реализовать) метод toString() класса.
В самой документации Java API говорится: «Рекомендуется, чтобы все подклассы переопределяли этот метод».
Блох, в Effective Java есть пункт «Всегда переопределять toString». И только дурак противоречит Блоху, верно?
Однако я начинаю сомневаться в этом совете: действительно ли стоит реализовывать toString() для сущностных классов?
Попробую изложить свои рассуждения.
Ан Объект entity имеет уникальный идентификатор; он никогда не совпадает с другим объектом, даже если эти два объекта имеют эквивалентные значения атрибутов. То есть (для ненулевого x) к классу сущностей применяется следующий инвариант (по определению):
Метод toString() возвращает строку, которая «текстуально представляет» свой объект (словами Java API).
хорошее представление отражает суть объекта, поэтому, если два представления различны, они являются представлениями разных (неэквивалентных) объектов, и наоборот, если два представления эквивалентны, они являются представлениями эквивалентные объекты. Это предполагает следующий инвариант для хорошего представления (для ненулевых x, y):
то есть каждый объект сущности должен иметь уникальное текстовое представление, которое возвращает toString(). Некоторые классы сущностей будут иметь уникальное поле имени или числового идентификатора, поэтому их метод toString() может возвращать представление, включающее это имя или числовой идентификатор. Но в целом метод toString() не имеет доступа к такому полю.
Без уникального поля для сущности лучшее, что может сделать toString(), — это включить поле, которое вряд ли будет одинаковым для разных объектов. Но это именно то, что требуется для System.identityHashCode(), что и обеспечивает Object.toString().
Таким образом, Object.toString() подходит для объекта сущности, который не имеет элементов данных, но для большинства классов вы захотите включить их в текстовое представление, верно? Фактически, вы захотите включить все из них: если тип имеет (не нулевой) элемент данных x, вы захотите включить в представление x.toString().
Но это создает проблему для элементов данных, которые содержат ссылки на другие сущности: то есть, которые являются ассоциациями. Если объект Person имеет элемент данных Person, простая реализация создаст фрагмент генеалогического древа этого человека, а не самого Person. Если существуют двусторонние ассоциации, наивная реализация будет рекурсивно выполняться до тех пор, пока не произойдет переполнение стека. Так может быть, пропустить элементы данных, содержащие ассоциации?
Но как насчет типа значения «Брак», имеющего элементы данных «Муж» и «Жена»? Об этих ассоциациях должен сообщать Marriage.toString(). Самый простой способ заставить работать все методы toString() — это Person.toString() сообщать только поля идентификаторов (
Таким образом, кажется, что предоставленная реализация toString() на самом деле не так уж плоха для классов сущностей. В таком случае зачем его переопределять?
public final class Person {
public void marry(Person spouse)
{
if (spouse == this) {
throw new IlegalArgumentException(this + " may not marry self");
}
// more...
}
// more...
}
Насколько полезным будет переопределение toString() при отладке исключения IlegalArgumentException, вызванного Person.marry()?
Постоянно рекомендуется переопределять (реализовать) метод toString() класса.
[list] [*]В самой документации Java API говорится: «Рекомендуется, чтобы все подклассы переопределяли этот метод». [*]Блох, в Effective Java есть пункт «Всегда переопределять toString». И только дурак противоречит Блоху, верно? [/list]
Однако я начинаю сомневаться в этом совете: действительно ли стоит реализовывать toString() [b]для сущностных классов[/b]?
Попробую изложить свои рассуждения.
[list] [*]Ан Объект entity имеет уникальный идентификатор; он никогда не совпадает с другим объектом, даже если эти два объекта имеют эквивалентные значения атрибутов. То есть (для ненулевого x) к классу сущностей применяется следующий инвариант (по определению):
[code]x.equals(y) == (x == y)[/code] [*]Метод toString() возвращает строку, которая «текстуально представляет» свой объект (словами Java API). [*]хорошее представление отражает суть объекта, поэтому, если два представления различны, они являются представлениями разных (неэквивалентных) объектов, и наоборот, если два представления эквивалентны, они являются представлениями эквивалентные объекты. Это предполагает следующий инвариант для хорошего представления (для ненулевых x, y):
[code]x.toString().equals(y.toString()) == x.equals(y)[/code] [*]Таким образом, для объектов, которые мы ожидаем [code]x.toString().equals(y.toString()) == (x == y)[/code] то есть каждый объект сущности должен иметь уникальное текстовое представление, которое возвращает toString(). Некоторые классы сущностей будут иметь уникальное поле имени или числового идентификатора, поэтому их метод toString() может возвращать представление, включающее это имя или числовой идентификатор. Но в целом метод toString() не имеет доступа к такому полю. [*]Без уникального поля для сущности лучшее, что может сделать toString(), — это включить поле, которое вряд ли будет одинаковым для разных объектов. Но это именно то, что требуется для System.identityHashCode(), что и обеспечивает Object.toString(). [*]Таким образом, Object.toString() подходит для объекта сущности, который не имеет элементов данных, но для большинства классов вы захотите включить их в текстовое представление, верно? Фактически, вы захотите включить все из них: если тип имеет (не нулевой) элемент данных x, вы захотите включить в представление x.toString(). [*]Но это создает проблему для элементов данных, которые содержат ссылки на другие сущности: то есть, которые являются ассоциациями. Если объект Person имеет элемент данных Person, простая реализация создаст фрагмент генеалогического древа этого человека, а не самого Person. Если существуют двусторонние ассоциации, наивная реализация будет рекурсивно выполняться до тех пор, пока не произойдет переполнение стека. Так может быть, пропустить элементы данных, содержащие ассоциации? [*]Но как насчет типа значения «Брак», имеющего элементы данных «Муж» и «Жена»? Об этих ассоциациях должен сообщать Marriage.toString(). Самый простой способ заставить работать все методы toString() — это Person.toString() сообщать только поля идентификаторов ([code]Person.name[/code] или System.identityhashCode(this)) Person. [*]Таким образом, кажется, что предоставленная реализация toString() на самом деле не так уж плоха для классов сущностей. В таком случае зачем его переопределять? [/list]
Чтобы конкретизировать, рассмотрим следующий код:
[code]public final class Person {
public void marry(Person spouse) { if (spouse == this) { throw new IlegalArgumentException(this + " may not marry self"); } // more... }
// more... } [/code]
Насколько полезным будет переопределение toString() при отладке исключения IlegalArgumentException, вызванного Person.marry()?