遵守包含 class 名称的变量的 DRY 原则

respecting the DRY principle for variable containing the class name

如何设置包含 class 名称的变量,例如 android.util.Log 中的 TAG,同时尊重 Dont-Repeat-Yourself?

这些是一些可能性:

Google代码中,经常用like

public class Classname {
    public final String TAG = "Classname";

它重复了 class 名称,并且在 AndroidStudio 中没有正确重构重命名(没有 String 是)。

然后,有一个动态变体

public class Classname {
    public final String TAG = getClass().getName();

不重复类名,看起来更好,但可读性较差。

或者,您可以制作 TAG static(这可能是过早的优化)。除了上面的官方版本,你可以在代码中获取名称,如

public class Classname {
    public final static String TAG 
       = new Object() { }.getClass().getEnclosingClass().getName();

可读性较差,并且确实存在继承问题(静态)。

这方面的最佳做法是什么?

还有比1-3更好的方法吗? (或者这是一种错误的做法?)

我过去使用过动态方法:

public class Classname {
     public final String TAG = getClass().getName();

不是不可读,而是自成体系。

对于更复杂的 DRY-ness 情况,始终可以创建自己的注释,然后

  • 使用两步编译过程首先生成非 DRY 源,然后将它们编译成非 DRY .class 文件。
  • 在程序的初始化步骤中使用注解在 运行 时间填充非 DRY 部分,然后再 运行 依赖于这些部分的任何逻辑。

使用第二种方法,你可以得到类似

的东西
@ReplaceWithClassName("TAG")
public class Classname {
     public final String TAG;

然后你将遍历所有用 @ReplaceWithClassName 注释的 classes 作为初始化步骤填充空白(更多关于遍历带注释的 classes here; more on changing a final String here ).

注解、内省和代码生成提供了极大的灵活性和强大的功能。因此,如果您使用它们,请明智地使用它们。对于这种特殊情况,"dynamic approach" 更具可读性。

动态检索 class 名称

@JeffMiller 在他的 sormula 项目的 ClassLogger class 中给出了示例。在classLogger中,他使用

StackTraceElement[] stes =  new Throwable().getStackTrace();
int e = stes.length - 1;
for (int i = 0; i < e; ++i) {
    if (stes[i].getClassName().equals(classLoggerClassName)) {
        // next on stack is the class that created me
        log = LoggerFactory.getLogger(stes[i + 1].getClassName());
        break;
    }
}

获取来电者的 class 姓名。

使用 class 获取其名称

@FlorentBayle 在评论中说

public final static String TAG = Classname.class.getName();

应该正确重构。 (并且比上面的变体 3 更具可读性)。

这也是 SLF4J 等第三方日志记录框架使用的方法。它通过

初始化
Logger logger = LoggerFactory.getLogger(HelloWorld.class);