I'm working on a project that has an extensive tree of generic inheritance and dependencies. Go to edit to see better example. The basics look something like this:
class A {
...
}
class B {
...
}
class C extends B {
...
}
class D<T extends B> extends A {
...
}
class StringMap<T extends A> {
HashMap<String, T> _elements;
...
}
So now I'm going to write a class that contains a specific StringMap
type.
class X {
StringMap<D<C>> _thing = new StringMap<D<C>>;
...
}
So far this all works fine. D<C>
is actually a very long name and the specific combination is going to show up very frequently in other parts of the code, so I decided to a class for the specific combination so it will be clearer and have a shorter name.
class DC extends D<C> {
}
//and go to update X
class X {
StringMap<D<C>> _thing = new StringMap<D<C>>(); //still works fine
StringMap<DC> _thing = new StringMap<DC>(); //error
...
}
Eclipse gives the error of
Bound mismatch: The type
DC
is not a valid substitute for the bounded parameter<T extends A>
of the typeStringMap<T>
So the question is, why does this not just work? DC
does nothing but extend D<C>
and echo the constructors. Why does StringMap
see DC
as different when it is just a child class of something it excepts?
EDIT:
OK, reworked the example to be closer to what I'm actually doing. I tested it and it does produce the error. What I'm doing here is using the generic type to ensure that clone()
returns the correct class for whoever implements it down the inheritance tree. Then in subclasses, I'm using B<T extends B<T>>
to ensure that subclasses of B
are passing in a subclass of B as the generic type T
.
public abstract class Undoable<T> implements Comparable<T> {
public abstract T clone();
public abstract void updateFields(T modified);
}
abstract public class A<T extends A<T, U>, U extends Comparable<U>>
extends Undoable<T> {
abstract U getKey();
@Override
public int compareTo(T element)
{
return getKey().compareTo(element.getKey());
}
}
public class B<T extends B<T>> extends A<T, String> {
@Override
public T clone()
{
// TODO Auto-generated method stub
return null;
}
@Override
public void updateFields(T modified)
{
// TODO Auto-generated method stub
}
@Override
String getKey()
{
// TODO Auto-generated method stub
return null;
}
}
public class C extends B<C> {
}
public class D<T extends B<T>> extends A<D<T>, String> {
@Override
String getKey()
{
// TODO Auto-generated method stub
return null;
}
@Override
public D<T> clone()
{
// TODO Auto-generated method stub
return null;
}
@Override
public void updateFields(D<T> modified)
{
// TODO Auto-generated method stub
}
}
public class DC extends D<C> {
}
public class StringMap<T extends Undoable<T>> {
HashMap<String, T> _elements;
}
public class Main {
public static void main(String[] args)
{
StringMap<D<C>> _thing = new StringMap<D<C>>(); //works
StringMap<DC> _thing1 = new StringMap<DC>(); //error
//Bound mismatch: The type DC is not a valid substitute for
//the bounded parameter <T extends Undoable<T>> of the type StringMap<T>
}
}
I'm not really sure why you want to do this and what you want to use the StringMap for exactly, but changing the StringMap definition to this will allow what you've done to compile:
Which means that the type T must be an Undoable of any type as long as that type is a contravariant of T (indeed T itself). So now you can do this:
Having said that, this is just a theoretical exercise - i would strongly recommend you avoid using such complex inheritance hierarchies, but its difficult to suggest an alternative without the full use case.
I hope that helps!
You must be doing wrong something else as the following works fine:
Try to post such a class reproducing your error.
As mentioned, your code is OK, although if i can guess, you meant to write the following line, which will indeed cause an error:
The reason is the same as that which causes the following to be a problem:
The generic type parameter given to a class when defining an identifier's type, i.e. in the lvalue, cannot be instantiated, i.e. in the rvalue, by a type whose parameter inherits the parameter given in the lvalue. If the parameters are different, the types are not considered compatible, even though intuitively they should have been (if generics were implemented in the language a little differently).
It's a bit of a gotcha...
Hmmm. I hope this isn't ancient old
, but I'm solving a problem I also had. I noticed that in D, D must extend B or C must extend D due to a loop-around in A., enough to make me put in a bunch of effort for no one to notice. The problem is in the StringMap class, which strictly wants its own input to extend a generic class whose generic must be its input.D works because D is the same class mentioned within the extension. It's safe. D refers to itself. D extends A, String>. Making a StringMap with it is good because D implements Undoable> because D extends A, String>, which extends Undoable>. DC, on the other hand must also implement Undoable, which impossible due to it only extending D; a solution could be for it to also implement Undoable where a new definition of methods with DC is needed to work. This is the real problem you are encountering. I hope this solves your problem.
My old problem: