How to force a generic type parameter to be an interface?
Is there a way in java to specify, that the type parameter of a generic class must be an interface (not just extending it!)
What I want to do is the following:
public class MyClass<X extends SomeInterface, Y extends SomeOtherClass & X>
Meaning that Y must be a subclass of SomeOtherClass AND implement X. What I currently get by the compiler is
The type X is not an interface; it cannot be specified as a bounded parameter
So, how can I tell the compiler that X must always be an interface?
Edit:
OK, I guess I oversimplified my problem a bit. Let's use my actual application domain to make it more clear:
I have an API for representing diagrams. A Diagram contains Node and Edge objects. All these three classes implement the Shape interface. Shapes may have child shapes, a parent shape and belong to a diagram.
The thing is, that I need to make two versions of this API: one open-source with just basic functionality and an extended one with more features. However, the extended API must only provide methods, which return the extended types (ExtendedDiagram, ExtendedNode, ExtendedEdge and (here comes the problem) ExtendedShape).
So I have something like this:
/* BASIC CLASSES */
public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{
public List<X> getChildShapes();
public X getParent();
public Y getDiagram();
...
}
public class Diagram<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
public class Edge<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
...
/* EXTENDED CLASSES */
public interface ExtendedShape extends Shape<ExtendedShape,ExtendedDiagram> { ... }
public class ExtendedDiagram extends Diagram<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
public class ExtendedEdge extends Edge<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
...
The extended API works fine and the basic API code gives some warnings, but the main problem occurs when using the basic API:
public class SomeImporter<X extends Shape<X,Y>, Y extends Diagram<X,Y>, E extends Edge<X,Y>>{
private Y diagram;
public void addNewEdge(E newEdge){
diagram.addChildShape(newEdge);
...
That last line gives me the following warning:
The method addChildShape(X) in the type Diagram is not applicable for the arguments (E)
So now, I would just like to specify that E also needs to implement X and all would be fine - I hope ;)
Does all that make sense? Do you guys know a way to do that? Or is there even a better way to get the extended API with the said restrictions?
Thanks for sticking with me, any help is greatly appreciated!
You can use:
class Foo<T extends Number & Comparable> {...}
A class Foo with one type parameter, T. Foo must be instantiated with a type that is a subtype of Number and that implements Comparable.
In the generics context, <Type extends IInterface>
handles both extends and implements. Here's an example:
public class GenericsTest<S extends Runnable> {
public static void main(String[] args) {
GenericsTest<GT> t = new GenericsTest<GT>();
GenericsTest<GT2> t2 = new GenericsTest<GT>();
}
}
class GT implements Runnable{
public void run() {
}
}
class GT2 {
}
GenericsTest will accept GT because it implements Runnable. GT2 does not, therefore it fails when trying to compile that second GenericsTest instantiation.
Maybe you can simplify your model a bit: too much generics become quickly a real pain in terms of readability, and that's quite an issue if you define a public API. Usually, if you can't understand anymore what should be inside the brackets, then you're going too far for your need - and you can't expect users to understand it better than yourself...
Anyway, in order to make your code compile, you may try defining something like this, in the Shape
type:
public <S extends Shape<?,?>> void addChildShape(S shape);
That should do it.
HTH
You wrote the following:
public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{
public List<X> getChildShapes();
public X getParent();
public Y getDiagram();
...
}
I would advise, at the minimum, getting rid of the X type variable, as follows:
public interface Shape<Y>{
public List<Shape<Y>> getChildShapes();
public Shape<Y> getParent();
public Diagram<Y> getDiagram();
...
}
The reason being is that what you originally wrote suffers from a potentially unbounded recursive nesting of the type parameters. A shape may be nested within a parent shape, which may be nested within another, all of which must be accounted for in the type signature... not a good recipe for readability. Well, it doesn't happen quite that way in your example, in which you declare "Shape<X>" instead of "Shape<Shape<X>>" but that's the direction you're going in, if you ever wanted to actually use Shape on its own...
I would probably also recommend going one step further and getting rid of the Y variable for similar reasons. Java generics don't cope very well with this sort of composition. When attempting to enforce static types for this type of modelling via generics, I've found that the type system starts to break down when you start to extend things later on.
It's typical of the Animal/Dog problem... Animal has a getChildren(), but Dog's children must also be Dogs... Java doesn't cope with this well because (in part due to the lack of abstract types as in languages like Scala, but I'm not saying you should rush off and use Scala for this problem either) the type variables have to start being declared in all sorts of places where they don't really belong.