如何在 java 项目中查找哪些数据 class 未覆盖 toString() 方法

How to find which data class not overriden toString() method in java project

在我们的项目中,某些数据 classes (POJO used for API Request & Response) 覆盖 toString() 方法以提供有意义的信息 - 但某些数据 classes 覆盖toString().

当应用程序打印数据 class 的 logs 时,其中 toString() 未被覆盖,它们没有打印有意义的信息,它们只是调用对象 class toString().

所以我们要识别那些数据 class 并提供 toString() implementation.

有什么方法可以识别 class 其中 toString() 方法不是 implemented.

查看每个数据 class 并检查 toString() 方法是一项乏味且耗时的任务。

有没有更好的方法可以使用一些工具(如 eclipse 或其他工具)来做到这一点?

使用反射。在我的项目中,我为每个域对象编写测试。测试检查 toString 方法是来自 Object class 还是来自我的 class。该测试的代码

/**
     * 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();
    }

然后有 class SomeClass,在 SomeClassTest 我按如下方式调用它

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

如果您想识别所有未覆盖 toString 的 classes,您可以编写工具来扫描 classpath 并为每个 class(来自您的包裹)。但这种方法有局限性。并非所有 classes 都需要 toString。服务 classes、REST 控制器等不需要 toString。所以自动化会给你很多误报。您可以仅扫描带有模型 classes 的包(如果您将模型保存在单独的包中)或使用自定义注释对模型 classes 进行注释(然后仅扫描带注释的 classes),但我认为这样的解决方案不值得付出努力。

最后,你可以使用https://projectlombok.org/ https://immutables.github.io/ or https://github.com/google/auto/tree/master/value 这些库使用代码 generation/anotation 处理来自动生成 equals/hashcode/toString。我使用 Immutables,它工作得很好。甚至将遗留数据模型迁移到 Immutables 也很简单。

在我的项目中,我为每个域对象编写了这样的测试。

当 class 未覆盖 toString() - 它必须使用默认实现,其中 包含 class 名称。因此:检查您的日志并向后工作!

或者,您可以使用反射:

  • 启动一个 JVM 运行,它包含 所有 你的 classes 在 classpath
  • 扫描所有你的 class的class路径(或者如果可能的话:那些类似于你的 POJO)
  • 检查每个 class 是否实现了 toString()

显然这需要大量工作 - 但没有技术原因说明为什么这不可行。当然,您的主要问题可能是减少要查看的 classes 的数量 - 包含 500 classes 的列表也不会太有帮助。

还有一个(可能的)选项:创建一个使用 反射 来实现 toString() 和 equals()/hashCode() 的 PojoBase class - 以及然后只需确保所有 POJO classes 扩展该基 class。 (免责声明:当然,您必须了解这样做的后果。但我们最近创建了这样一个 BaseBean,它使用 Apache Commons EqualsBuilder,...以这种方式调用)

一种方法是使用 Reflections Library 获取给定包中的所有 classes,请注意,这不适用于 classes,如匿名、私有、部分、不可访问, ..等,所以最好的方法是根据 @GhostCat answer

手动完成

现在你应该走这条路,这是如何完成的,首先通过 Reflections Library 获得 classes,引用自 this answer by @Staale

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

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

然后遍历 classes,并检查 toString 是否在 class 自身中声明

for(Class clazz : allClasses)
{
    if(!clazz.getMethod("toString").getDeclaringClass().getName().equals(clazz.getName()))
        System.out.println("toString not overridden in class "+clazz.getName());
}

如果您的数据 class 具有共同的抽象 class 它们从中继承,那么这个想法就可行。

在你的super中引入一个摘要toString()class:

public abstract String toString();

这将强制所有子classes 覆盖它,如果他们不这样做,您将得到编译错误。

遗憾的是,这种方法有一个主要的限制。如果你有这样的事情:

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

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

class C extends B {
}

它不会注意到 C 中缺少的 toString()

这种方法可能不适合你,但我想我还是会 post,所以也许它会对阅读你的问题的其他人有所帮助。

实际上应该很简单,只要检查你是否可以通过 .getDeclaredMethod("toString") 检索 toString 方法,如果这个语句触发 java.lang.NoSuchMethodException,那么它不会在你的焦点 class 中声明.