2

In our project certain data classes (POJO used for API Request & Response) are overridding the toString() method to provide meaningful information - but certain data classes are not overridden toString().

When application print the logs of data class in which toString() is not overridden, they are not printing meaningful information they just call object class toString().

So we want to identify those data class and provide toString() implementation.

Is there any way to identify those kind of class in which toString() method is not implemented.

Looking in every data class and checking for toString() method is tedious and time consuming task.

Is there any better way to do that using some tools like eclipse or something else?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
Snehal Patel
  • 1,282
  • 2
  • 11
  • 25

5 Answers5

3

When the class is not overriding toString() - it must be using the default implementation which contains the class name. Thus: check your logs and work backwards!

Alternatively, you can make use of reflection:

  • start a JVM run that contains all your classes in the classpath
  • scan the class path for all your classes (or if possible: those that resemble your POJOs)
  • check each class if it implements toString()

Obviously that is a lot of work - but there is no technical reason why this shouldn't work out. Of course your main problem here might be to reduce the number of classes to look at - a list that contains 500 classes wouldn't be too helpful either.

And one other (potential) option: create a PojoBase class that uses reflection to implement toString() and maybe equals()/hashCode() - and then simply ensure that all your POJO classes extend that base class. (Disclaimer: of course you have to understand the consequences of doing such things. But we lately create such a BaseBean that is using the Apache Commons EqualsBuilder, ... calls this way)

Khaled.K
  • 5,828
  • 1
  • 33
  • 51
GhostCat
  • 137,827
  • 25
  • 176
  • 248
2

One way is to use Reflections Library to obtain all classes within a given package, note that this does not work on classes like anonymous, private, partial, inaccessible, ..etc, so the best way is to do it manually as per @GhostCat answer

Now should you go this route, here's how it could be done, first obtain classes through Reflections Library, quoted from this answer by @Staale

Reflections reflections = new Reflections("my.project.prefix");

Set<Class<? extends Object>> allClasses =
            reflections.getSubTypesOf(Object.class);

Then iterate over the classes, and check if toString is declared in the class itself

for(Class clazz : allClasses)
{
    if(!clazz.getMethod("toString").getDeclaringClass().getName().equals(clazz.getName()))
        System.out.println("toString not overridden in class "+clazz.getName());
}
Khaled.K
  • 5,828
  • 1
  • 33
  • 51
2

Here is an idea that works if your data classes have a common abstract class they inherit from.

Introduce an abstract toString() in your superclass:

public abstract String toString();

This will force all subclasses to override it and you will get compile errors if they don't.

Sadly this method has a major constraint. If you have something like this:

abstract class A {
    public abstract String toString();
}

class B extends A {
    public String toString() {
        return "foo";
    }
}

class C extends B {
}

it will not notice the missing toString() in C.

This method might not work for you but I thought I'd post it anyway so maybe it'll help others who read your question.

André Stannek
  • 7,773
  • 31
  • 52
1

Use reflection. In my project, I write test for each domain object. Test checks if toString method comes from Object class or from my class. Code for that test

/**
     * Passes if class has overridden toString method. Fails otherwise.
     */
    public static void toStringTest(Object o) throws NoSuchMethodException {
        Class<?> clazz = o.getClass().getMethod("toString").getDeclaringClass();
        assertThat(clazz).isNotEqualTo(Object.class)
                .withFailMessage("expecting method toString to be defined in actual class");
        assertThat(o.toString()).isNotEmpty();
    }

Then having class SomeClass, In SomeClassTest I invoke it as follows

   public void shouldOverrideToString() throws NoSuchMethodException {
        TestUtils.toStringTest(new SomeClassTest());
    }

If you want to identify all classes that do not override toString, you may write tool that will scan classpath and invoke such test for every class (from your package). But this approach has limitations. Not all classes require toString. Service classes, REST controllers, etc do not need toString. So automation will give you a lot of false positives. You may scan only packages with model classes (if you keep model in separate package) or annotate model classes with custom annotation (and then scan only annotated classes) but I think such solutions are not worth the effort.

Finally, you may use https://projectlombok.org/ https://immutables.github.io/ or https://github.com/google/auto/tree/master/value Those libraries use code generation/anotation processing to auto-generate equals/hashcode/toString. I use Immutables and it works very well. Even migration legacy data model to Immutables is straightforward.

In my project, I write such test for every domain object.

Bartosz Bilicki
  • 12,599
  • 13
  • 71
  • 113
  • `new SomeClassTest()`: This means that the class must be known. OP clearly states that he wants to quickly identify those classes that don't override the `toString` method. – Seelenvirtuose Jul 26 '17 at 06:30
0

Actually should be quite easy, just check if you can retrieve the toString method by .getDeclaredMethod("toString") , if this statement trigger the java.lang.NoSuchMethodException, then it's not declared in your focused class.

luca84
  • 13
  • 3