Постоянно рекомендуется переопределить (реализовать) метод toString()
класса.
- документация по Java API сам говорит: «Рекомендуется, чтобы все подклассы переопределяли этот метод.».
- Блох в Эффективной Java имеет пункт «Всегда заменять toString». А Блоху противоречит только дурак, не так ли?
Однако я сомневаюсь в этом совете: действительно ли стоит реализовывать toString()
для классов сущностей?
Постараюсь изложить свои рассуждения.
Объект entity имеет уникальный идентификатор; он никогда не совпадает с другим объектом, даже если два объекта имеют эквивалентные значения атрибутов. То есть (для ненулевого x) следующий инвариант применяется для класса сущности (по определению):
x.equals(y) == (x == y)
Метод
toString()
возвращает строку, которая «текстуально представляет» свой объект (говоря словами Java API).хорошее представление захватывает сущность объекта, поэтому, если два представления различны, они являются представлениями разных (неэквивалентных) объектов, и наоборот, если два представления эквивалентны, они являются представлениями эквивалентных объектов. Это предполагает следующий инвариант для хорошего представления (для ненулевых значений x, y):
x.toString().equals(y.toString()) == x.equals(y)
Таким образом, для сущностей мы ожидаем
x.toString().equals(y.toString()) == (x == y)
, то есть каждый объект-сущность должен иметь уникальное текстовое представление, которое возвращаетtoString()
. Некоторые классы сущностей будут иметь уникальное имя или числовое поле идентификатора, поэтому их методtoString()
может возвращать представление, которое включает это имя или числовой идентификатор. Но вообще методtoString()
не имеет доступа к такому полю.Без уникального поля для объекта лучшее, что может сделать
toString()
, - это включить поле, которое вряд ли будет одинаковым для разных объектов. Но это в точности требование для _ 11_, что и предоставляетObject.toString()
.Итак,
Object.toString()
подходит для объекта сущности, не имеющего элементов данных, но для большинства классов вы хотели бы включить их в текстовое представление, верно? Фактически, вы хотите включить все из них: если тип имеет (не нулевой) член данных x, вы захотите включитьx.toString()
в представление.Но это создает проблему для элементов данных, содержащих ссылки на другие сущности, то есть на ассоциации. Если объект
Person
имеет член данныхPerson father
, наивная реализация создаст фрагмент генеалогического древа этого человека, а не самогоPerson
. Если есть двусторонние связи, наивная реализация будет рекурсивной, пока вы не получите переполнение стека Так что, возможно, пропустите элементы данных, которые содержат ассоциации?Но как насчет типа значения
Marriage
, имеющего элементы данныхPerson husband
иPerson wife
? Об этих ассоциациях следует сообщитьMarriage.toString()
. Самый простой способ заставить всеtoString()
методы работать - этоPerson.toString()
сообщать только поля идентификации (Person.name
илиSystem.identityhashCode(this)
)Person
.Таким образом, кажется, что предоставленная реализация
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()
?
"Illegal request for " + this + " to marry " + spouse + ": A Person cannot marry itself"
. Но в любом случае это не лучший пример того, когдаtoString()
был бы полезен, посколькуmarry()
имеет полный доступ к внутренним компонентамPerson
для передачи более осознанного сообщения. - person Mark Peters   schedule 03.02.2011Person
имена не уникальны, они бесполезны в качестве идентификаторов. Вы бы не использовали имя человека в качестве первичного ключа в таблице базы данных. - person Raedwald   schedule 31.03.2016