Jak wydrukować obiekt Java bez otrzymywania komunikatu „SomeType @ 2f92e0f4”?
Mam klasę zdefiniowaną w następujący sposób:
public class Person {
private String name;
// constructor and getter/setter omitted
}
Próbowałem wydrukować instancję mojej klasy:
System.out.println(myPerson);
ale mam następujący wynik: com.foo.Person@2f92e0f4
.
Podobna rzecz wydarzyła się, gdy próbowałem wydrukować tablicę Person
obiektów:
Person[] people = //...
System.out.println(people);
Mam wyjście: [Lcom.foo.Person;@28a418fc
Co oznacza ten wynik? Jak zmienić to wyjście, aby zawierało imię i nazwisko mojej osoby? Jak mogę wydrukować kolekcje moich obiektów?
Uwaga : to ma charakter kanoniczny pytań i odpowiedzi na ten temat.
Odpowiedzi
tło
Wszystkie obiekty Java mają toString()
metodę, która jest wywoływana podczas próby wydrukowania obiektu.
System.out.println(myObject); // invokes myObject.toString()
Ta metoda jest zdefiniowana w Object
klasie (nadklasie wszystkich obiektów Java). Object.toString()
Metoda zwraca dość brzydki patrząc ciąg, składający się z nazwą klasy, a @
symbolem i hashcode obiektu w systemie szesnastkowym. Kod do tego wygląda następująco:
// Code of Object.toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Wynik taki com.foo.MyType@2f92e0f4
można zatem wytłumaczyć jako:
com.foo.MyType
- nazwa klasy, czyli klasa znajduje sięMyType
w pakieciecom.foo
.@
- łączy ze sobą sznurek2f92e0f4
kod skrótu obiektu.
Nazwy klas tablic wyglądają trochę inaczej, co jest dobrze wyjaśnione w Javadocach dla Class.getName()
. Na przykład [Ljava.lang.String
oznacza:
[
- tablica jednowymiarowa (w przeciwieństwie do[[
lub[[[
itp.)L
- tablica zawiera klasę lub interfejsjava.lang.String
- rodzaj obiektów w tablicy
Dostosowywanie wyjścia
Aby wydrukować coś innego, kiedy zadzwonić System.out.println(myObject)
, należy zastąpić ten toString()
sposób w swojej własnej klasie. Oto prosty przykład:
public class Person {
private String name;
// constructors and other methods omitted
@Override
public String toString() {
return name;
}
}
Teraz, jeśli drukujemy a Person
, widzimy raczej ich nazwę niż com.foo.Person@12345678
.
Należy pamiętać, że toString()
jest to tylko jeden ze sposobów konwersji obiektu na łańcuch. Zwykle te dane wyjściowe powinny w pełni opisywać twój obiekt w jasny i zwięzły sposób. Lepszym rozwiązaniem toString()
dla naszej Person
klasy może być:
@Override
public String toString() {
return getClass().getSimpleName() + "[name=" + name + "]";
}
Który wydrukowałby np Person[name=Henry]
. To naprawdę przydatna część danych do debugowania / testowania.
Jeśli chcesz skupić się tylko na jednym aspekcie obiektu lub zawrzeć dużo jazzy formatowania, lepiej zdefiniować oddzielną metodę, np String toElegantReport() {...}
.
Automatyczne generowanie wyniku
Wiele IDE oferuje obsługę automatycznego generowania toString()
metody na podstawie pól w klasie. Na przykład zobacz dokumentację dotyczącą Eclipse i IntelliJ .
Kilka popularnych bibliotek Java oferuje również tę funkcję. Oto kilka przykładów:
@ToString
adnotacja z Projektu Lombok
Drukowanie grup obiektów
Więc stworzyłeś miły toString()
dla swojej klasy. Co się stanie, jeśli ta klasa zostanie umieszczona w tablicy lub kolekcji?
Tablice
Jeśli masz tablicę obiektów, możesz wywołać Arrays.toString()
prostą reprezentację zawartości tablicy. Weźmy na przykład tę tablicę Person
obiektów:
Person[] people = { new Person("Fred"), new Person("Mike") };
System.out.println(Arrays.toString(people));
// Prints: [Fred, Mike]
Uwaga: jest to wywołanie metody statycznej wywołanej toString()
w klasie Arrays, która różni się od tego, o czym rozmawialiśmy powyżej.
Jeśli masz tablicę wielowymiarową , możesz użyć jej Arrays.deepToString()
do uzyskania tego samego rodzaju wyników.
Kolekcje
Większość kolekcji da ładny wynik w oparciu o wywołanie .toString()
każdego elementu.
List<Person> people = new ArrayList<>();
people.add(new Person("Alice"));
people.add(new Person("Bob"));
System.out.println(people);
// Prints [Alice, Bob]
Musisz więc tylko upewnić się, że elementy listy definiują ładne, toString()
jak omówiono powyżej.
Myślę, że apache zapewnia lepszą klasę użytkową, która zapewnia funkcję pobierania ciągu
ReflectionToStringBuilder.toString(object)
Każda klasa w Javie ma toString()
domyślnie metodę, która jest wywoływana, jeśli przekażesz jakiś obiekt tej klasy do System.out.println()
. Domyślnie to wywołanie zwraca nazwę klasy @ hashcode tego obiektu.
{
SomeClass sc = new SomeClass();
// Class @ followed by hashcode of object in Hexadecimal
System.out.println(sc);
}
Można przesłonić metodę toString klasy, aby uzyskać inne dane wyjściowe. Zobacz ten przykład
class A {
String s = "I am just a object";
@Override
public String toString()
{
return s;
}
}
class B {
public static void main(String args[])
{
A obj = new A();
System.out.println(obj);
}
}
W Eclipse, przejdź do swojej klasy, kliknij prawym przyciskiem myszy-> źródło-> Generuj toString()
;
Zastąpi toString()
metodę i wydrukuje obiekt tej klasy.
Wolę używać funkcji narzędzia, która używa GSON do deserializacji obiektu Java do łańcucha JSON.
/**
* This class provides basic/common functionalities to be applied on Java Objects.
*/
public final class ObjectUtils {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private ObjectUtils() {
throw new UnsupportedOperationException("Instantiation of this class is not permitted in case you are using reflection.");
}
/**
* This method is responsible for de-serializing the Java Object into Json String.
*
* @param object Object to be de-serialized.
* @return String
*/
public static String deserializeObjectToString(final Object object) {
return GSON.toJson(object);
}
}
W intellij możesz automatycznie wygenerować metodę toString, naciskając alt + inset, a następnie wybierając toString (), oto wyjście dla klasy testowej:
public class test {
int a;
char b;
String c;
Test2 test2;
@Override
public String toString() {
return "test{" +
"a=" + a +
", b=" + b +
", c='" + c + '\'' +
", test2=" + test2 +
'}';
}
}
Jak widać, generuje String przez konkatenację kilku atrybutów klasy, dla prymitywów wypisze ich wartości, a dla typów referencyjnych użyje ich typu klasy (w tym przypadku do metody łańcuchowej Test2).
Domyślnie każdy obiekt w Javie ma toString()
metodę, która wyprowadza ObjectType @ HashCode.
Jeśli chcesz uzyskać więcej znaczących informacji, musisz zastąpić toString()
metodę w swojej klasie.
public class Person {
private String name;
// constructor and getter/setter omitted
// overridding toString() to print name
public String toString(){
return name;
}
}
Teraz, gdy drukujesz obiekt osoby, używając System.out.prtinln(personObj);
go, wypisze imię osoby zamiast nazwy klasy i kodu skrótu.
W drugim przypadku, gdy próbujesz wydrukować tablicę, [Lcom.foo.Person;@28a418fc
wyświetla typ tablicy i jej kod skrótu.
Jeśli chcesz wydrukować nazwiska osób, istnieje wiele sposobów.
Możesz napisać własną funkcję, która iteruje każdą osobę i drukuje
void printPersonArray(Person[] persons){
for(Person person: persons){
System.out.println(person);
}
}
Możesz go wydrukować za pomocą Arrays.toString (). Wydaje mi się to najprostsze.
System.out.println(Arrays.toString(persons));
System.out.println(Arrays.deepToString(persons)); // for nested arrays
Możesz wydrukować go w sposób java 8 (używając strumieni i odniesienia do metody).
Arrays.stream(persons).forEach(System.out::println);
Mogą być też inne sposoby. Mam nadzieję że to pomoże. :)
Jeśli bezpośrednio wydrukujesz obiekt osoby, będzie ClassName@HashCode
to zgodne z Kodeksem.
w twoim przypadku com.foo.Person@2f92e0f4
jest drukowany. Where Person
jest klasą, do której należy obiekt i 2f92e0f4
jest hashCode obiektu.
public class Person {
private String name;
public Person(String name){
this.name = name;
}
// getter/setter omitted
@override
public String toString(){
return name;
}
}
Teraz, jeśli spróbujesz użyć obiektu, Person
to wypisze nazwę
Class Test
{
public static void main(String... args){
Person obj = new Person("YourName");
System.out.println(obj.toString());
}
}
Jeśli spojrzymy na klasę Object (klasa nadrzędna wszystkich klas w Javie), implementacja metody toString () to
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
za każdym razem, gdy drukujesz dowolny obiekt w Javie, zostanie wywołana toString (). Teraz to od Ciebie zależy, czy zastąpisz toString (), a Twoja metoda wywoła inne wywołanie metody klasy Object.
Udało mi się to zrobić, używając Jackson
wiosny 5. W zależności od obiektu Jackson może nie działać we wszystkich przypadkach.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
ObjectMapper mapper = new ObjectMapper();
Staff staff = createStaff();
// pretty print
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(staff);
System.out.println("-------------------------------------------------------------------")
System.out.println(json);
System.out.println("-------------------------------------------------------------------")
wynik byłby podobny
-------------------------------------------------------------------
{
"id" : 1,
"internalStaffId" : "1",
"staffCms" : 1,
"createdAt" : "1",
"updatedAt" : "1",
"staffTypeChange" : null,
"staffOccupationStatus" : null,
"staffNote" : null
}
-------------------------------------------------------------------
Więcej przykładów Jackson
tutaj
Możesz GSON
też spróbować . Powinno wyglądać mniej więcej tak:
Gson gson = new Gson();
System.out.println(gson.toJson(objectYouWantToPrint).toString());