0

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?)

serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • 4
    You're free to do as you want. DRY is a principle, it's not a law that requires you to bend over backwards to try to avoid writing the same sequence of letters twice. – Kayaman Jun 11 '15 at 10:20
  • 2
    What about `public final static String TAG = Classname.class.getName();`, this should be refactored correctly ? – Florent Bayle Jun 11 '15 at 10:26
  • @Kayaman: It seems inelegant to repeat the classname. Sure, it's not a law, but if the code could be better ... – serv-inc Jun 11 '15 at 10:35
  • 2
    @user1587329 Java isn't exactly the go-to (goto?) language for elegant code in the first place. – Kayaman Jun 11 '15 at 10:37
  • 1
    A better solution would be for the logger to determine the class name so that there would be no need for TAG. I've written a logger wrapper that works this way. – Jeff Miller Jun 11 '15 at 13:18
  • 1
    @user1587329: [ClassLogger](http://www.sormula.org/javadoc/org/sormula/log/ClassLogger.html) in my ORM project, [sormula](http://www.sormula.org), does this. Also see [project in bitbucket](https://bitbucket.org/jeff43017/sormula/overview). It is not written for Android but I am sure it could be adapted for Android. – Jeff Miller Jun 11 '15 at 18:49
  • @FlorentBayle: that works in a `static` context, which is nice. – serv-inc Jun 12 '15 at 11:00

2 Answers2

1

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.

Community
  • 1
  • 1
tucuxi
  • 17,561
  • 2
  • 43
  • 74
  • How would you approach `final static String TAG`? (f.ex. in a utility class?) (or do you avoid all utility classes, as [http://stackoverflow.com/questions/3339929/if-a-utilities-class-is-evil-where-do-i-put-my-generic-code?](evil) – serv-inc Jun 11 '15 at 13:03
  • the link on "more on changing a final String" demonstrates how to change a `private static final` one - so it *is* possible to do this via introspection using run-time annotations as part of an initialization step. On the other hand, I would still prefer "way 3" from your original question. Read on http://en.wikipedia.org/wiki/Technical_debt - avoiding debt is good, but your programming time is limited. See also http://en.wikipedia.org/wiki/KISS_principle – tucuxi Jun 11 '15 at 13:11
  • Thanks a bunch for the annotation possibility, the assurance to use the dynamic approach and the reminder to Keep It Simple Stupid (and the Wiki link saying that the original is *without a comma*). (The `private static final` approach had so many warning signs that using it in production code without *having to* seems overkill) – serv-inc Jun 11 '15 at 13:19
1

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);
Community
  • 1
  • 1
serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • 1
    Use this to initialize log wrapper per class. classLoggerClassName in this example is log wrapper class name, org.sormula.log.Logger. RoboGuice looks promising. I will investigate it for my next Android app. – Jeff Miller Jun 14 '15 at 16:45
  • @JeffMiller: RoboGuice use seems discouraged by Google: http://developer.android.com/training/articles/memory.html. See f.ex. this on SO: http://stackoverflow.com/questions/24194283/does-avoid-dependency-injection-frameworks-in-the-android-memory-guide-apply-t/24194366#24194366 – serv-inc Jun 19 '15 at 11:34