0

I am using a Map to store references to a class of a generic type parameter and an implementation object with the same generic type parameter, more specifically:

public class SomeImpl<T extends BaseClass> {

    void execute(T instance);
}

Now, in another class, i want to store references to implementations based on their generic type parameter.

private Map<Class<BaseClass>, SomeImpl<BaseClass>> map;

However, i find it somewhat confusing to operate with the generics here, especially since a lot of unsafe cast warnings are shown.

private <T extends BaseClass> void register(SomeImpl<T> impl, Class<T> aClass) {
    // unsafe
    Class<BaseClass> configClass = (Class<BaseClass>) aClass; 
    // unsafe
    map.put((SomeImpl<BaseClass>) impl, configClass);
}

and later when i want to retrieve some instance

private SomeImpl<BaseClass> get(Class<? extends BaseClass> aClass) {
    // unsafe
    return (SomeImpl<BaseClass>) map.get(aClass); 
}

Could someone clarify if the above statements are safe, thus the warnings can safely be ignored?

Glains
  • 2,773
  • 3
  • 16
  • 30
  • @Nikolas Sorry, changed it to `impl` – Glains Aug 06 '18 at 14:22
  • What is `binders`? I suggest it's `map`. – Nikolas Charalambidis Aug 06 '18 at 14:23
  • @Nikolas Yes, it is. Thanks for the hint! – Glains Aug 06 '18 at 14:24
  • Have you tried `private static void register(SomeImpl impl, Class aClass) { }` and `private static SomeImpl get(Class aClass) { }`? The map stores `BaseClass` anyway. – Nikolas Charalambidis Aug 06 '18 at 14:27
  • @Nikolas Of course, this will work fine, but then i have the unchecked casts somewhere else, when calling these methods from outside. – Glains Aug 06 '18 at 14:33
  • See https://stackoverflow.com/q/44422685/2891664 for examples of this kind of thing, although I don't know if you can use any of those directly since I don't know what `BaseClass` and `SomeImpl` are. I also don't know enough about your types involved to say for sure if the code in your question is type-safe or not, although it probably isn't. – Radiodef Aug 06 '18 at 15:08
  • @Radiodef I have posted an answer to my question that takes your suggestions into account. Could you take a look? I would mark it as accepted then. – Glains Aug 06 '18 at 18:31
  • Possible duplicate of [Heterogeneous container to store genericly typed objects in Java](https://stackoverflow.com/questions/6139325/heterogeneous-container-to-store-genericly-typed-objects-in-java) – Glains Aug 06 '18 at 20:55

1 Answers1

0

In the end i did go with the following solution. Thanks a lot to Radiodef, who pointed me to the use of an type-safe heterogeneous container.

private Map<Class<?>, SomeImpl<?>> map = new HashMap<>();

And the following methods will add/get the concrete implementation:

public <T extends BaseClass> void addImpl(SomeImpl<T> loader, Class<T> aClass) {
    map.put(type, loader);
}

public <T extends BaseClass> SomeImpl<T> getImpl(Class<T> aClass) {
    return (SomeImpl<T>) map.get(type);
}

Originally, i was also looking for a method that can be invoked with any BaseClass. I figured out that this is not possible with that pattern, so the following did the trick:

public SomeImpl<BaseClass> getBaseImpl(Class<? extends BaseClass> aClass) {
    return (BaseLoader<BaseClass>) map.get(type);
}

And this is indeed a truely unsave cast, since the compiler cannot ensure that the correct type of BaseClass is passed. However, i can ensure that the correct type is always passed (or force it using isAssignableFrom).

This allows me to load some BaseClass objects, from a database for example:

List<BaseClass> list = getSomeBaseClasses();

Map<Class<? extends BaseClass>, List<BaseClass> map = list.stream()
    .collect(Collectors.groupingBy(BaseClass::getClass));

map.forEach((aClass, bases) -> {
    SomeImpl<BaseClass> impl = getBaseImpl(aClass);
    impls.forEach(impl::execute);
}
Glains
  • 2,773
  • 3
  • 16
  • 30
  • I would make your `getBaseImpl` method return `SomeImpl extends BaseClass>`. Like I said in my other comment, I don't know enough about what you're doing to know if casting to `SomeImpl` is safe. There are some specific circumstances where a cast like that is okay (for example [see this answer which talks about casting `Optional`](https://stackoverflow.com/a/44853739/2891664)), but it usually isn't. You should be able to call the regular generic `getImpl` with any class argument extending `BaseClass`, though, even a wildcard like `Class extends BaseClass>`. – Radiodef Aug 06 '18 at 18:42
  • @Radiodef The problem is that i have to use the `BaseClass` (as i fetch multiple subclasses with Hibernate, i have created a minimal example [here](https://ideone.com/Q6nTgX). Currently it does not compile, since `SomeImpl extends BaseClass>` cannot be called with `BaseClass` as an argument. – Glains Aug 06 '18 at 19:02
  • I suppose i could open a new question as well with that. – Glains Aug 06 '18 at 19:04
  • Since the `BaseLoader` is basically a consumer, you could use a wildcard capturing helper method to call `load`, like in my answer. (I was editing the answer a bit earlier, so if you didn't see that example before, there's some extra stuff now.) Here's a fork of your Ideone example with this: https://ideone.com/j7YFCi – Radiodef Aug 06 '18 at 19:33
  • Thanks a lot, i was also able to find sonething similar here: https://stackoverflow.com/questions/6139325/heterogeneous-container-to-store-genericly-typed-objects-in-java – Glains Aug 06 '18 at 20:54