Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over heterogeneous container

Tags:

java

generics

I am using a heterogeneous container similar to this one. I can put and receive objects from the container with ease:

Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
String someString = f.getFavorite(String.class);

But there seem to be no easy way to iterate over such container. I can add a keySet() method to the Favorites class and simply return the key set of the internal Map object:

public Set<Class<?>> keySet() {
  return favorites.keySet();
}

Now, I would like to iterate over the keys, use the keys to get the associated values, and call some methods on the received objects:

for (Class<?> klass : f.keySet()) {
   // f.getFavorite(klass).<SOME_METHOD_SPECIFIC_TO_THE_CLASS-KEY>
}

I thought that I could access the methods of the objects held in my container by calling klass.cast(f.getFavorite(klass)).SOME_METHOD(), but it doesn't work either (meaning, I cannot access any methods except for the Object-related methods).

Let's say, that in my use case I would like to inspect the interfaces of all these objects I iterate over and act accordingly to the detected interface. Let's also assume that I may have dozens of objects of various classes and all of them implement one of three interfaces.

The only solution I can think of is to stuff my code with dozens of isinstance checks, but I would prefer a less cumbersome approach (i.e. checking if a given object implements one of three interfaces).

like image 236
Wojciech Walczak Avatar asked Feb 24 '16 16:02

Wojciech Walczak


1 Answers

By trying to call a specific method on each entry, you are basically saying that you know better than the compiler, and that you know each entry has a specific super class.

If you know that's the case, you can use Class#asSubclass to type klass as Class<? extends KnownSuper> so that getFavorite will then return a subclass of KnownSuper (and therefore expose the method):

Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod()
for (Class<?> klass : f.keySet()) {
    f.getFavorite(klass.asSubClass(superClass)).callMethod()
}

However, this will obviously give a runtime exception if one of the key classes does not extend KnownSuper. So if the above would be safe, you should parameterize your heterogeneous container to only accept key classes that extend from KnownSuper in the first place.

If not all entries will be of this type, you could also check first if the key is suitable when iterating:

Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod()
for (Class<?> klass : f.keySet()) {
    if (superClass.isAssignableFrom(klass)) {
        f.getFavorite(klass.asSubClass(superClass)).callMethod()
    }
}
like image 170
Mark Peters Avatar answered Oct 02 '22 18:10

Mark Peters