Ошибка: несовместимые типы: коллекция<DB_Account> не может быть преобразована в коллекцию<Account>

Следующее дает эту ошибку:

public class Account
{
    ...
}

public class DB_Account extends Account implements DBObject
{
    ...
}

public class Cache<E extends DBObject>
{
    protected Map<Long,E> m_contents;

    ...

    public Collection<E> values()
    {
        return m_contents.values();
    }
}

public class DB_Imp implements Database
{
    protected Cache<DB_Account> m_accounts;

    ...

    @Override
    public Collection<Account> getAccounts()
    {
        if (m_accounts.isValid())
            /* Compiler gives error here */
            return m_accounts.values();
        ...
    }
}

В настоящее время я работал над ошибкой компилятора в DB_Imp класс, добавив Collections.class.cast() вокруг вызова accounts.values() и добавил @SuppressWarnings, Должен быть лучший способ. Другой способ - изменить класс Cache на:

    @SuppressWarnings("unchecked")
    public <T> Collection<T> values()
    {
        return Collections.class.cast(m_contents.values());
    }

2 ответа

Проблема в том, что вы пытаетесь вернуть Collection<DB_Account> как Collection<Account> компилятор не позволит вам. Collection не является ковариантным, так Collection<DB_Account> не является подтипом Collection<Account>,

Способ исправить это будет изменить метод values в Database чтобы:

Collection<? extends Account> values();

РЕДАКТИРОВАТЬ: Если вы не можете сделать это изменение, то я бы вместо этого сделал:

@Override
public Collection<Account> getAccounts() {
    if (m_accounts.isValid())
      return Collections.unmodifiableCollection(m_accounts.values());
    ...
}

Это создает неизменяемое представление о values() с правильным типом параметра (Collection<Account>). И это дает вам дополнительную безопасность (во время выполнения), что ваш Cache не может быть изменено кодом клиента. values() возвращает вид на Cache карта, так что каждое изменение, внесенное в него кем-то, звонящим getAccounts() будет отражать на Cache, Это то, что вы обычно хотите избежать.

Сначала посмотрите на ошибку. Это говорит о том, что:

  • За Cache<DB_Account> m_accountsвызов метода m_accounts.values() возвращает Collection<DB_Account>,
  • Ands getAccounts() возвращается Collection<Account>
  • Но они не полиморфны: Collection<Account> не супертип Collection<DB_Account>и поэтому компилятор не может конвертировать их.

Это абсолютно правильно и ожидаемо.

  • Для супертипа X
  • И подтип Y
  • Foo<Y> не является подтипом Foo<X>

Это важнейшее понятие в дженериках Java, и его стоит понять. Вы должны прочитать Учебные руководства Java> Обобщения, Наследование и Подтипы.

Возможные решения

Трудно предложить решение, не зная точного контекста. Один из возможных подходов, если вы можете изменить Database интерфейс, будет менять getAccounts использовать подстановочный знак

public Collection<? extends Account> getAccounts();

Однако это только позволит вам получить предметы из возвращенных Collection, не устанавливайте их в него (возможно, это то, что вы хотите). Чтобы понять, почему вы должны прочитать "Верхний ограниченный шаблон" и "Рекомендации по использованию шаблонов".

Другие вопросы по тегам