Может кто-нибудь уточнить ковариантные типы возвращаемых данных в Java (6)? - PullRequest
4 голосов
/ 05 марта 2012

Я думаю Я спрашиваю о ковариантных типах возвращаемых данных.У меня есть сгенерированный код, который я пытаюсь расширить и использовать.Давайте предположим, что у меня есть следующие два класса:

public class SuperParent
{
    public List<SuperChild> getList()
    {
        return new ArrayList<SuperChild>();
    }
}
public class SuperChild
{
}

Теперь я хочу извлечь из них новые классы следующим образом:

public class SubParent extends SuperParent
{
    public List<SubChild> getList()
    {
        return new ArrayList<SubChild>();
    }
}
public class SubChild extends SuperChild
{
}

Проблема в том, что, очевидно, я не могу переопределитьМетод getList (), поскольку возвращаемый тип не совпадает, несмотря на то, что оба класса расширяются в одном направлении.Может кто-нибудь объяснить?

Ответы [ 5 ]

5 голосов
/ 05 марта 2012

Ваше понимание co-variant верно, а использование - нет. List<SubChild> не совпадает с List<SuperChild>

Учтите, что List<Animals> - это не то же самое, что List<Dogs>, и все может пойти ужасно неправильно, если это будет разрешено. Dog - это Animal, но если ему было разрешено присваивать, как показано ниже:

List<Dogs> dogs = new ArrayList<Dogs>();
List<Animals> animals = dogs; //not allowed.

что происходит, когда вы добавляете в нее кошку?

animals.add(new Cat());

и

Dog dog = dogs.get(0); //fail

Так что это не разрешено.

Как и многие другие, используйте List<? extends SuperChild> в качестве типа возврата, чтобы решить вашу проблему.

EDIT На ваш комментарий выше, если у вас нет контроля над суперклассом, боюсь, вы ничего не сможете сделать.

3 голосов
/ 05 марта 2012

Проблема в том, что с шаблонами List<SuperChild> и List<SubChild> несовместимы, поскольку, если бы вы вызывали getList() на экземпляре SubParent, но через интерфейс SuperParent, вы получили бы возвращаемое значение типа List<SuperChild>. Это позволит вам добавить другие экземпляры SuperChild , даже если список может содержать только экземпляры SubChild (согласно типу возврата, определенному в SubParent).

Чтобы этот компилятор изменил тип возвращаемого значения на List<? extends SuperChild>, т.е.

public class SuperParent
{
  public List<? extends SuperChild> getList()
  {
    return new ArrayList<SuperChild>();
  }
}

Это позволит вам возвращать списки подтипов, но не позволит вам добавлять элементы в список, возвращаемый с использованием супертипа (то есть вы не можете добавлять элементы в List<? extends SuperChild>.

2 голосов
/ 05 марта 2012

Как отмечали другие, List<SubChild> не является подклассом List<SuperChild>.

В зависимости от того, что вы хотите сделать, вы можете использовать дженерики:

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
{
}
2 голосов
/ 05 марта 2012

List<SubChild> является не подклассом List<SuperChild>

В дженериках java нет никакой дисперсии.

Итак, когда вы пытаетесь комбинировать возвращаемый тип, это на самом деле другой тип, и java не позволяет вам полностью его изменить [так как это не будет безопасно].

Ваш метод getList() в SubParent должен вернуть List<SuperChild> [или ArrayList<SuperChild>, ...] для решения этой проблемы.

0 голосов
/ 05 марта 2012

Представьте себе что-то вроде этого:

SubParent subParent = new SubParent();
SuperParent superParent = (SuperParent) subParent; // upcast is okay
List<SuperChild> list = superParent.getList();
list.add(new SuperChild());

Последнее утверждение будет нарушать договор SubParent.

Исправление будет заключаться в изменении контракта getList SuperParent на List<? extends SuperChild> getList().

...