如何在运行时在 Java 中找到类的实例?

bar*_*ara 5 java reflection

假设我有一个带有注释的类,例如:

@MyConfig
class MyConfiguration {

    @MyParameter
    String parameter;
}
Run Code Online (Sandbox Code Playgroud)

如果我知道这个类的一个实例存在(例如,一个是在另一个线程中构造的),我如何才能在其他地方获得对该实例的引用。我试图通过它的@Annotation 找到该实例。

dim*_*414 4

您不能简单地根据对象的类型或注释想象出对对象的引用,您也不应该真正想要这样做。造成这种情况的主要原因是垃圾收集——当对象超出范围时,JVM 会为您清理内存;如果您可以动态创建新引用,垃圾收集器将无法安全地清理任何内容,并且您会很快耗尽内存。

也就是说,您可以通过多种方法来构建您所描述的非常简单的功能,以便您可以按对象的类型查找对象。

Map最简单(也可以说是最好)的方法是使用 a (考虑Guava的)来简单地注册您想要的实例ClassToInstanceMap。虽然您必须显式添加到映射中,但这实际上在代码划分方面对您来说会更加清晰。即使您将缓存行为设置为注释上的静态方法或类似的方法,将构造与缓存分开也是一个很好的实践。

// somewhere accessible to both the constructing and accessing code, such as a
// public static field on the Annotation
Map<Class<? extends Annotation>,Object> annotationMap = new HashMap();

// wherever the instance is constructed
annotationMap.put(MyConfig.class, new MyConfiguration());

// wherever the instance is needed
MyConfiguration myConf = (MyConfiguration)annotationMap.get(MyConfig.class);
Run Code Online (Sandbox Code Playgroud)

您可能已经注意到这包含Object值,因为理论上任何类都可以被注释,所以我们必须显式转换。假设您强制将哪些类型插入到映射中,这将起作用,但它很脆弱。说实话,将注释与实例关联起来的想法本身很脆弱,因此这可能是您最不用担心的。


如果您想确保最近构建的内容MyConfiguration可以像这样访问,您可以将上面的内容放入其构造函数中,如下所示:

@MyConfig
class MyConfiguration {
  public MyConfiguration() {
    // note this is potentially dangerous, as this isn't finished constructing
    // yet so be very cautious of this pattern, even though it might seem cleaner
    annotationMap.put(MyConfig.class, this);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以确信,如果MyConfiguration实例存在,则可以通过其带注释的类型访问它annotationMap


然而,正如我上面所暗示的,我怀疑这些对您来说都不是好的解决方案。真正的原因是因为注释根本不是为了让您引用实例而设计的;相反,它们的目的是让您在拥有某个实例后了解有关该实例的信息。那么我问你,为什么你认为你需要通过注释来查找对象?您可以使用另一种模式吗?

我怀疑您真正想要构建的是单例- 您希望运行时只有一个 实例MyConfiguration,并且您希望所有代码都能轻松访问它。一个标准模式是:

@MyConfig
class MyConfiguration {
  private static MyConfiguration INSTANCE = null;

  public static MyConfiguration getInstance() {
    // note this is not thread-safe
    // see the above link for several thread-safe modifications
    if(INSTANCE == null) {
      INSTANCE = new MyConfiguration();
    }
    return INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)

这允许任何代码调用MyConfiguration.getInstance()并能够访问该实例。也就是说,单例通常被认为是不好的做法(尽管不如您所描述的那样)。理想情况下,您应该将配置实例传递给任何需要它的类或线程。显式传递引用,而不是依赖半神奇的缓存或像单例这样的全局状态,无疑是处理您所面临的问题的“正确”方法。