respecting the DRY principle for variable containing the class name

240 views Asked by At

How do you set variables which contain the class name, like TAG in android.util.Log, while respecting Dont-Repeat-Yourself?

These are some possibilities:

In Google code, it is often used like

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

which repeats the classname and was not refactor-renamed correctly in AndroidStudio (no Strings were).

Then, there is a dynamic variant

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

which does not repeat the Classname, thus seems better, yet is less readable.

Or, you could make TAG static (this might be premature optimization). Apart from the official version above, you could get the name in code like

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

which is way less readable, and does have problems with inheritance (being static).

What is the best practice concerning this?

Is there a better way than 1-3? (Or is this a wrong approach?)

2

There are 2 answers

3
tucuxi On BEST ANSWER

I have gone with the dynamic approach in the past:

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

It is not that unreadable, and it is self-contained.

For more complex cases of DRY-ness, there is always the possibility of creating your own annotations, and then either

  • Use a two-step compile process to first generate non-DRY sources and then compile them into non-DRY .class files.
  • Use the annotations in an initialization step within your program to fill in the non-DRY parts at run-time, before running any logic that depends on those parts.

Using the second approach, you could have something like

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

And then you would iterate through all classes annotated with @ReplaceWithClassName filling in the blanks as an initialization step (more on iterating through annotated classes here; more on changing a final String here).

Annotations, introspection and code-generation provide great flexibility and power. Therefore, use wisely if you use them at all. For this particular case, the "dynamic approach" is much more readable.

2
serv-inc On

retrieve class name dynamically

@JeffMiller gave the example in the ClassLogger class of his sormula project. In the class Logger, he uses

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;
    }
}

to get the caller's class name.

use the class to get its name

@FlorentBayle said in the comments that

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

should be refactored correctly. (And is more readable than variant 3 above).

This is also the approach used by third-party logging frameworks like SLF4J. It is initialized via

Logger logger = LoggerFactory.getLogger(HelloWorld.class);