Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can find an instance of class at runtime in Java?

Suppose I have a class with an annotation, e.g.:

@MyConfig
class MyConfiguration {

    @MyParameter
    String parameter;
}

If I know an instance of this class exists (for instance, one was constructed in another thread) how can I get a reference to the instance elsewhere. I'm trying to find the instance by its @Annotation.

like image 669
barbara Avatar asked Oct 01 '22 00:10

barbara


1 Answers

You cannot simply conjure up a reference to an object based on its type or annotations, nor should you really want to. The primary reason for this is garbage collection - the JVM cleans up memory for you as objects fall out of scope; if you could create new references dynamically, the garbage collector would not be able to safely clean anything up, and you'd rapidly run out of memory.

That said there's many ways you can build up functionality like you're describing pretty simply, so that you can look up an object by its type.

The easiest (and arguably best) way to do this is to simply register the instance you want, using a Map (consider Guava's ClassToInstanceMap). While you have to explicitly add to the map, that is actually going to be a lot cleaner for you in terms of code-compartmentalization. Even if you make the caching behavior a static method on the annotation, or something like that, separating construction from caching is a good practice to get into.

// 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);

You've likely noticed that this holds Object values, because any class can theoretically be annotated, so we have to explicitly cast. This will work, assuming you enforce what types are inserted into the map, but it is fragile. Truth be told, the idea of associating annotations with instances is fragile in itself, so this is likely the least of your worries.


If you want to ensure that the most recently constructed MyConfiguration is accessible like this, you could put the above in it's constructor, like so:

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

Now you can be confident that, if a MyConfiguration instance exists, it is accessible from annotationMap by its annotated type.


As I hinted above however, I suspect neither of these are good solutions for you. And really the reason why is because annotations are not designed at all to let you refer to instances; they are instead meant to let you know things about an instance once you already have one. So let me ask you, why do you think you need to lookup an object by its annotation? Is there another pattern you can use instead?

I suspect what you're really trying to build is a Singleton - you expect your runtime to have exactly one instance of MyConfiguration, and you want all your code to easily access it. A standard pattern for this is:

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

This lets any code call MyConfiguration.getInstance() and be able to access the instance. That said, Singletons are generally considered bad practice (though less so than what you're describing). Ideally, you should be passing your configuration instance around to whatever classes or threads need it. Passing your references explicitly, rather than relying on a semi-magical cache or global state like a singleton, is far and away the "right" way to deal with the problem you're facing.

like image 183
dimo414 Avatar answered Oct 03 '22 03:10

dimo414