Методы объектов Java: toString ()

Введение В этой статье я начну серию статей, описывающих часто забываемые методы базового класса Object языка Java. Ниже приведены методы базового объекта Java, которые присутствуют во всех объектах Java из-за неявного наследования объекта. Ссылки на каждую статью этой серии включаются для каждого метода по мере публикации статей. * toString (вы здесь) * getClass [https://stackabuse.com/javas-object-methods-getclass/] * равно [https: // s

Вступление

В этой статье я начну серию статей, описывающих часто забываемые методы базового класса Object языка Java. Ниже приведены методы базового объекта Java, которые присутствуют во всех объектах Java из-за неявного наследования объекта. Ссылки на каждую статью этой серии включаются для каждого метода по мере публикации статей.

В следующих разделах я опишу, что это за методы, их базовые реализации и как их переопределить при необходимости. Основное внимание в этой первой статье уделяется toString() который используется для предоставления строкового представления, которое идентифицирует экземпляр объекта и передает его содержимое и / или значение в удобочитаемой форме.

Метод toString ()

На первый взгляд метод toString() может показаться довольно бесполезным методом, и, честно говоря, его реализация по умолчанию не очень полезна. По умолчанию метод toString() возвращает строку, в которой указано имя класса, за которым следует знак @, а затем шестнадцатеричное представление ячейки памяти, которой был назначен экземпляр объекта.

Чтобы помочь в моем обсуждении вездесущих методов Java Object, я буду работать с простым Person , определенным следующим образом:

 package com.adammcquistan.object; 
 
 import java.time.LocalDate; 
 
 public class Person { 
 private String firstName; 
 private String lastName; 
 private LocalDate dob; 
 
 public Person() {} 
 
 public Person(String firstName, String lastName, LocalDate dob) { 
 this.firstName = firstName; 
 this.lastName = lastName; 
 this.dob = dob; 
 } 
 
 public String getFirstName() { 
 return firstName; 
 } 
 
 public void setFirstName(String firstName) { 
 this.firstName = firstName; 
 } 
 
 public String getLastName() { 
 return lastName; 
 } 
 
 public void setLastName(String lastName) { 
 this.lastName = lastName; 
 } 
 
 public LocalDate getDob() { 
 return dob; 
 } 
 
 public void setDob(LocalDate dob) { 
 this.dob = dob; 
 } 
 } 

Наряду с этим классом у меня есть элементарный Main для запуска примеров, показанных ниже, чтобы представить функции базовой реализации toString() .

 package com.adammcquistan.object; 
 
 import java.time.LocalDate; 
 
 public class Main { 
 public static void main(String[] args) { 
 Person me = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23")); 
 Person me2 = new Person("Adam", "McQuistan", LocalDate.parse("1987-09-23")); 
 Person you = new Person("Jane", "Doe", LocalDate.parse("2000-12-25")); 
 System.out.println("1. " + me.toString()); 
 System.out.println("2. " + me); 
 System.out.println("3. " + me + ", " + you); 
 System.out.println("4. " + me + ", " + me2); 
 } 

Результат выглядит так:

 1. [email protected] 
 2. [email protected] 
 3. [email protected] , [email protected] 
 4. [email protected] , [email protected] 

Прежде всего следует упомянуть, что выходные данные для первой и второй строк идентичны, что показывает, что когда вы передаете экземпляр объекта таким методам, как print , println , printf , а также средствам ведения журнала, неявно вызывается метод toString()
Кроме того, этот неявный вызов toString() также происходит во время конкатенации, как показано в выводе строки 3.

Хорошо, теперь пришло время высказать свое личное мнение, когда дело доходит до лучших практик программирования на Java. Что выделяется для вас как потенциально беспокоящее в строке 4 (на самом деле, любой результат в этом отношении)?

Надеюсь, вы отвечаете на такой вопрос: «Ну, Адам, приятно, что вывод сообщает мне имя класса, но что, черт возьми, мне делать с этим бессмысленным адресом памяти?».

И я отвечал: «Ничего!». Это на 99,99% бесполезно для нас, программистов. Намного лучше было бы переопределить эту реализацию по умолчанию и предоставить что-то действительно значимое, например:

 public class Person { 
 // omitting everyting else remaining the same 
 
 @Override 
 public String toString() { 
 return "<Person: firstName=" + firstName + ", lastName=" + lastName + ", dob=" + dob + ">"; 
 } 
 } 

Теперь, если я перезапущу предыдущий класс Main, я получу следующий значительно улучшенный результат:

 1. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23> 
 2. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23> 
 3. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>, <User: firstName=Jane, lastName=Doe, dob=2000-12-25> 
 4. <Person: firstName=Adam, lastName=McQuistan, dob=1987-09-23>, <User: firstName=Adam, lastName=McQuistan, dob=1987-09-23> 

МОЙ БОГ! То, что я могу прочитать! Благодаря этой реализации у меня теперь есть шанс действительно понять, что происходит в файле журнала. Это особенно полезно, когда техподдержка кричит о беспорядочном поведении экземпляров People в программе, за которую я на крючке.

Предостережения при реализации и использовании toString ()

Как показано в предыдущем разделе, реализация информативного toString() в ваших классах - довольно хорошая идея, поскольку он обеспечивает способ осмысленной передачи содержимого и идентичности объекта. Однако бывают случаи, когда вы захотите применить несколько иной подход к их реализации.

Например, предположим, что у вас есть объект, который просто содержит слишком много состояния для упаковки в вывод метода toString() или когда объект в основном содержит набор служебных методов. В этих случаях часто рекомендуется выводить простое описание класса и его намерений.

Рассмотрим следующий бессмысленный служебный класс, который находит и возвращает самого старого человека в списке объектов People.

 public class OldestPersonFinder { 
 public List<Person> family; 
 
 public OldestPersonFinder(List<Person> family) { 
 this.family = family; 
 } 
 
 public Person oldest() { 
 if (family.isEmpty()) { 
 return null; 
 } 
 Person currentOldest = null; 
 for (Person p : family) { 
 if (currentOldest == null || p.getDob().isAfter(currentOldest.getDob())) { 
 currentOldest = p; 
 } 
 } 
 return currentOldest; 
 } 
 
 @Override 
 public String toString() { 
 return "Class that finds the oldest Person in a List"; 
 } 
 } 

В этом случае было бы не очень полезно перебирать всю коллекцию объектов Person List и строить какую-то смехотворно большую строку для возврата, представляющую каждого Person . Вместо этого гораздо более целесообразно вернуть строку, описывающую намерения класса, который в этом случае должен найти Person который является самым старшим.

Еще одна вещь, которую я хотел бы настоятельно предложить, - убедиться, что вы предоставляете доступ ко всей информации, относящейся к данным вашего класса, которые вы включаете в вывод для вашего метода toString()

Скажем, к примеру, я не предоставил геттер для моего Person класса dob члена в тщетной попытке сохранить возраст человека тайну. К сожалению, пользователи моего Person в конечном итоге поймут, что они могут просто проанализировать вывод метода toString() и получить данные, которые они ищут таким образом. Теперь, если я когда-нибудь изменю реализацию toString() я почти наверняка сломаю их код. С другой стороны, позвольте мне сказать, что, как правило, анализировать toString() - плохая идея именно по этой причине.

Заключение

В этой статье описываются использование и значение часто забываемого toString() базового класса Java Object. Я объяснил поведение по умолчанию и высказал свое мнение о том, почему я считаю, что лучше всего реализовать ваше собственное поведение, специфичное для класса.

Как всегда, спасибо за чтение и не стесняйтесь комментировать или критиковать ниже.

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus