end-user
end-user

Reputation: 2947

Can someone clarify covariant return types in Java(6)?

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

Answers (5)

Ravi Bhatt
Ravi Bhatt

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

Puce
Puce

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

devconsole
devconsole

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

Thomas
Thomas

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

amit
amit

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

Related Questions