Reputation: 2947
I think I'm asking about covariant return types. I have some generated code that I'm trying to extend and use. Let's suppose I have the following two classes:
public class SuperParent
{
public List<SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
public class SuperChild
{
}
Now, I want to derive new classes from these thusly:
public class SubParent extends SuperParent
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
The problem is, apparently I can't override the getList() method because the return type doesn't match, despite both classes being extended in the same direction. Can someone explain?
Upvotes: 4
Views: 1187
Reputation: 3163
Your understanding of co-variant
is correct but usasge is not. List<SubChild>
is not the same as List<SuperChild>
Consider this, List<Animals>
is not the same as List<Dogs>
and things can go horribly wrong if that was allowed. A Dog
is an Animal
but if it was allowed to assign like below:
List<Dogs> dogs = new ArrayList<Dogs>();
List<Animals> animals = dogs; //not allowed.
then what happens when you add a cat to it?
animals.add(new Cat());
and
Dog dog = dogs.get(0); //fail
So its not allowed.
As sugested by many others, use List<? extends SuperChild>
as return type to solve your problem.
EDIT To your comment above, if you do not have control over super class, i am afraid, you can not do anything.
Upvotes: 5
Reputation: 38142
As others pointed out List<SubChild>
is not a subclass of List<SuperChild>
.
Depending on what you want to do, you could use generics:
public class SuperParent<T extends SuperChild>
{
public List<T> getList()
{
return new ArrayList<T>();
}
}
public class SuperChild
{
}
public class SubParent extends SuperParent<SubChild>
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
Upvotes: 2
Reputation: 7915
Imagine something like this:
SubParent subParent = new SubParent();
SuperParent superParent = (SuperParent) subParent; // upcast is okay
List<SuperChild> list = superParent.getList();
list.add(new SuperChild());
The last statement would violate the contract of SubParent.
A fix would be to change the contract of SuperParent's getList to List<? extends SuperChild> getList()
.
Upvotes: 0
Reputation: 88727
The problem is that with generics List<SuperChild>
and List<SubChild>
are not compatible, since if you'd call getList()
on a SubParent
instance but through a SuperParent
interface, you'd get a return value of type List<SuperChild>
. This would allow you to add other instances of SuperChild
even though the list is only allowed to contain instances of SubChild
(as per the return type defined in SubParent
).
To make this compile change the return type to List<? extends SuperChild>
, i.e.
public class SuperParent
{
public List<? extends SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
This would allow you to return lists of subtypes but would not allow you to add elements to the list returned using the super type (i.e. you can't add elements to a List<? extends SuperChild>
.
Upvotes: 3
Reputation: 178491
List<SubChild>
is not an subclass of List<SuperChild>
There is no co-variance in java's generics.
So, when you try to co-variant the return type, it is actually a different type, and java does not allow you to change it completely [since it will not be safe].
Your method getList()
in SubParent
should return List<SuperChild>
[or ArrayList<SuperChild>
, ...] to solve this issue.
Upvotes: 2