Джава:Получение свойств класса для построения строкового представления
-
21-09-2019 - |
Вопрос
Допустим, у меня есть такой класс (а также предположим, что все частные переменные:
public class Item {
private String _id = null;
private String _name = null;
private String _description = null;
...
}
Теперь, если я хочу создать представление этого класса toString(), я бы сделал что-то подобное внутри класса Item:
@Override
public String toString() {
return (_id + " " + _name + " " + _description);
}
Но что, если внутри класса у меня есть, скажем, 15 частных переменных?Должен ли я писать имя каждой переменной, как это?
В идеале хотелось бы справиться с задачей, перебрав список приватных переменных этого класса и сконструировав строковое представление:
@Override
public String toString() {
ArrayList<String> members = getClass().getMembers(); //Some method like this
String string = "";
for(...)
string += members[i] + " ";
}
Или, возможно, метод toJSON, мне все равно понадобится доступ к именам этих переменных.Какие-либо предложения?
Решение
Вы можете сделать:
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName());
sb.append(": ");
for (Field f : getClass().getDeclaredFields()) {
sb.append(f.getName());
sb.append("=");
sb.append(f.get(this));
sb.append(", ");
}
return sb.toString();
}
Не используйте конкатенацию строк для создания конечного результата из 15 элементов данных, особенно если toString()
будут называть много.Фрагментация памяти и накладные расходы могут быть очень высокими.Использовать StringBuilder
для создания больших динамических строк.
Обычно я использую свою IDE (IntelliJ) для простого создания toString()
методы для меня, а не использовать для этого отражение.
Еще один интересный подход заключается в использовании Аннотация @ToString из Project Lombok:
import lombok.ToString; @ToString(excludes="id") public class ToStringExample { private static final int STATIC_VAR = 10; private String name; private Shape shape = new Square(5, 10); private String[] tags; private int id; @ToString(callSuper=true, includeFieldNames=true) public static class Square extends Shape { private final int width, height; public Square(int width, int height) { this.width = width; this.height = height; } } }
Я нахожу это гораздо более предпочтительным, чем, скажем, сборщики строк toString из Jakarta Commons, потому что этот подход гораздо более настраиваемый и также создается во время компиляции, а не во время выполнения.
Другие советы
Проверьте этот API org.apache.commons.lang.builder.ToStringBuilder, он предоставляет несколько способов создания toString с отражением или без отражения.Обратите внимание и на другие подклассы.
Есть такое апи, и оно называется Java-отражение
Чтобы выполнить то, что вы запрашиваете, вы можете просто сделать что-то вроде:
Class<?> cls = this.getClass();
Field fieldlist[] = cls.getDeclaredFields();
for (Field aFieldlist : fieldlist) {
// build toString output with StringBuilder()
}
Это должно быть именно то, что вы ищете
public String toString() {
StringBuilder sb = new StringBuilder();
try {
Class c = Class.forName(this.getClass().getName());
Method m[] = c.getDeclaredMethods();
Object oo;
for (int i = 0; i < m.length; i++)
if (m[i].getName().startsWith("get")) {
oo = m[i].invoke(this, null);
sb.append(m[i].getName().substring(3) + ":"
+ String.valueOf(oo) + "\n");
}
} catch (Throwable e) {
System.err.println(e);
}
return sb.toString();
}
Большинство IDE предоставляют способ создания toString
метод в данном классе.
Учитывая Item
класс с несколькими полями:
class Item {
int i;
int j;
int k;
int l;
int m;
int n;
int o;
}
Например, в Eclipse выполнение функции «Создать toString()» в Item
класс выше создаст следующее:
@Override
public String toString() {
return "Item [i=" + i + ", j=" + j + ", k=" + k + ", l=" + l + ", m="
+ m + ", n=" + n + ", o=" + o + "]";
}
С использованием отражение позволит программным способом наблюдать за собственными полями, но само отражение является довольно дорогостоящим (читай:медленный) процесс, поэтому, если это действительно не требуется, используя фиксированную toString
метод, написанный во время выполнения, вероятно, будет более желательным.
Вы можете столкнуться с проблемой скорости.Если вы используете отражение, оно может быть очень медленным по сравнению с запуском машинного кода.Если вы собираетесь использовать отражение, вам, вероятно, следует иметь в памяти кеш переменных, которые вы собираетесь перебирать.
Начиная с хороший медицинский ответ, вы можете использовать этот код для строкового представления всех полей объекта, даже если ты не из его класса (очевидно, это только для полей с методом получения):
/**Gives a string representation of the fields of the object
* @param obj the object
* @return
*/
private static String objectToString(Object obj) {
StringBuilder sb = new StringBuilder();
try {
Class c = Class.forName(obj.getClass().getName());
Method m[] = c.getDeclaredMethods();
Object oo;
for (int i = 0; i < m.length; i++)
if (m[i].getName().startsWith("get")) {
oo = m[i].invoke(obj, null);
sb.append(m[i].getName().substring(3) + "="
+ String.valueOf(oo) + "\n");
}
} catch (Throwable e) {
System.err.println(e);
}
return sb.toString();
}