Объект форматирования, используя отражение


Я делаю объект форматирования для использования при отладке.

Отформатирован класс:

package com.myname.somepackage;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// Allows a variable to be displayed when using Formatter.format
@Retention(RetentionPolicy.RUNTIME)
public @interface Formatted {

}

Праматерия класс:

package com.myname.somepackage;

import java.lang.reflect.Field;

public final class Formatter {
    private Formatter() {}

    // Returns a string containing the object's information, for debugging
    // Format: ClassName[var1=somevalue, var2=somevalue]
    // The object's variables must have the Formatted annotation to be displayed here
    public static String format(Object object) {
        String className = object.getClass().getSimpleName();
        Field[] fields = object.getClass().getDeclaredFields();
        String string = className + "[";
        for (Field field : fields) {
            field.setAccessible(true);
            Formatted annotation = field.getAnnotation(Formatted.class);
            if (annotation != null) {
                String varName = field.getName();
                try {
                    String value = field.get(object).toString();
                    string += varName + "=" + value + ", ";
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    string += varName + "=" + "{Unavailable}, ";
                }
            }
        }
        // remove last ", "
        if (string.endsWith(", "))
            string = string.substring(0, string.length() - 2);
        string += "]";
        return string;
    }
}

Класс для тестирования этого:

package com.myname.somepackage.math.geom.r2;

import com.myname.somepackage.Formatted;
import com.myname.somepackage.Formatter;

public final class Point2d {
    @Formatted
    private final double x, y;

    public Point2d(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }
    public Point2d setX(double x) {
        return new Point2d(x, this.y);
    }
    public double getY() {
        return this.y;
    }
    public Point2d setY(double y) {
        return new Point2d(this.x, y);
    }

    @Override
    public String toString() {
        return Formatter.format(this);
    }
}

Код, чтобы проверить его:

Point2d point = new Point2d(4, 2);
System.out.println(point);

Консоль затем выводит "Point2d[x=4.0, y=2.0]".

Как мой код выглядит? Я понимаю, что рефлексия является плохой, но это просто мой ленивый способ отладки. Спасибо



Комментарии
1 ответ

Это очень хорошая ММО, но может быть улучшена.

Если ваш проект использует Апач Коммонс (эта библиотека часто включен), вы должны рассмотреть возможность использования FieldUtils класса получить поля : https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.html

В частности, getFieldsWithAnnotation способ позволит сократить ваш код сложности немного.

Что до вас хоть ;)

В этой части :

try {
String value = field.get(object).toString();
string += varName + "=" + value + ", ";
} catch (IllegalAccessException e) {
e.printStackTrace();
string += varName + "=" + "{Unavailable}, ";
}

может произойти сбой, если ваше поле nullвы должны использовать String + operator чтобы избежать его такой :

string += varName + "=" + field.get(object) + ", ";

string действительно плохое имя для переменной, может переименовать его как res или что-то ?

Я не большой поклонник печатные, вы должны рассмотреть возможность использования различных лесозаготовки коммунальные услуги, предложенной на Java : https://docs.oracle.com/javase/9/docs/api/java/util/logging/Logger.html или slf4j.

Я думаю, что эти 4 модификации уже сделаем аккуратнее код, но мы можем сделать больше рефакторинга :
вместо того, чтобы использовать строку, мы конкатенируем по крупицам, а затем удалить последней запятой, вы должны рассмотреть возможность использования Stream над полями массива и генерировать результат с Collectors#joining метод.

В конце концов, у вас есть следующий метод :

private static final String SEPARATOR = ", ";

public static String format(Object object) {
final String className = object.getClass().getSimpleName();
final String prefix = className + "[";
String res = Arrays.stream(FieldUtils.getFieldsWithAnnotation(object.getClass(), Formatted.class))
.map(field -> {
String varName = field.getName();
try {
return varName + "=" + field.get(object);
} catch (IllegalAccessException e) {
log.severe(e.toString());
return varName + "=" + "{Unavailable}";
}
}).collect(joining(SEPARATOR));
return prefix + res + "]";
}

3
ответ дан 23 февраля 2018 в 11:02 Источник Поделиться