1

Making a API for internal use, I need some Java abstraction and I don't know how to do it.

  • There is an Interface A with derived interfaces.
  • Let's say one of the extended interfaces of A is called B.
  • And let's say there is an class C implementing B.

I have a Factory/Pool class F from which I want to get a list of instances (or set or similar collection). What I want is basically this:

List<B> instances = F.getAllSuitableInstances(parameters);

Get me all the instances specified by my parameters as a collection of B.

First attempt was with this function signature

public List<A> getAllSuitableInstances(parameters);

but when I try to cast the resulting List to a List for use, I get told "incompatible types" - even though B is derived from A.

So I tried what the java libraries do:

public <T> List<T> getAllSuitableInstances(parameters, T);

and try calling that with B as second parameter. But that doesn't work either, since T has to be supplied as an instance for some reason, and interfaces cannot have instances.

I know I could use <?> with casting but I'd like to make this as typesafe as possible.

So, what is the (best) way to get this done?

foo
  • 1,968
  • 1
  • 23
  • 35
  • You are right - answers to the question referenced above answer mine as well. But I didn't find that other question because I didn't look for its keywords (dog, animal, polymorphic). Maybe others will phrase their search more like I did, so the existence of this question might still be of use to somebody. – foo May 26 '14 at 00:59

2 Answers2

1

even though B is derived from A.

You have to be careful here -- Java's generics are invariant, meaning that Something<Superclass> is not a superclass of Something<Subclass> (e.g. List<Number> is not a superclass of List<Integer>). There are plenty of questions on SO about this, so I won't repeat information that other people can explain much more effectively, like here and here.

As for your second signature, it seems to me that the T in (parameters, T) is unnecessary.

public <T> List<T> getAllSuitableInstance(parameters);

should work, unless I'm misunderstanding your requirements. The disadvantage of this is that T could be anything, so you're somewhat limited in the methods you can invoke on objects of type T, but if you aren't doing that then no concern is warranted.

If the methods in your interfaces are necessary to get your method working (doesn't seem like it, but just in case), you can always add bounds (e.g. public <T extends A> List<T>...) to your generic type, so the compiler knows that all instances of T inside your method are type A or subtypes of A.

If you need to use the type of T in your method (say for instanceof checks), one option is passing in a Class object and using its methods to perform the runtime equivalent of instanceof checks and casting (among other options):

public <T> List<T> getAllSuitableInstance(parameters, Class<? extends T> clazz); // Might be able to use just Class<T> too

Hopefully that helps. Let me know if I got something wrong about what you needed.

awksp
  • 11,764
  • 4
  • 37
  • 44
  • That's it, thank you - the version with the bounds gives me some type safety, while allowing the required flexibility. Your links add value to this answer, making it more useful for other readers as well, so that's why I prefer (and accept) this answer. – foo May 24 '14 at 03:26
  • @foo No problem! Just a (probably redundant) note -- the version with `` is primarily useful for the work you do *inside* the method. If you use just plain `T`, I believe the compiler would be able to figure out that `T -> B`, but it might be a little more tricky to do your work inside the method because you have absolutely no idea what `T` is (unless it's part of one of your parameters, like in the version with the `Class` parameter). – awksp May 24 '14 at 03:31
1

This is a common misunderstanding when it comes to programming with generics, but it is an important concept to learn

when we have two concrete types A and B (for example, Number and Integer), MyClass<A> has no relationship to MyClass<B>, regardless of whether or not A and B are related.

The common parent of MyClass<A> and MyClass<B> is Object.

No need to even pass the class as you mentioned in second option

this should work for you

public <T> List<T> getAllSuitableInstances(parameters) {
List<T> list = new ArrayList<>();
 //content add to list
    return list;
}
Karibasappa G C
  • 2,686
  • 1
  • 18
  • 27